config/patches/base/mutter/Support-Dynamic-triple-double-buffering.patch

3760 lines
139 KiB
Diff

From 6b8d25eb6370a5040185ac83b44e32e3189e0533 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 10 Nov 2021 18:55:53 +0800
Subject: [PATCH 01/44] onscreen/native: Tidy up secondary GPU buffer tracking
The primary plane of a CRTC can only have one `current_fb` and therefore
only one `next_fb`, so don't confuse the situation by maintaining two of
each. In the past a single onscreen targeted multiple CRTCs meaning
separate tracking was necessary, but not anymore.
If we're using `COPY_MODE_ZERO` then the `imported_buffer` will reference
the importee internally. We don't need to keep a redundant reference to
the old buffer after importation.
If we're using `COPY_MODE_PRIMARY` or `COPY_MODE_SECONDARY_GPU` then we
only need to keep the final copy for the duration of scanout.
All of that happens before the flip so only one `next_fb` needs to live on
after the flip. And if there's only one `next_fb` then there's only one
`current_fb`.
In a related cleanup we now also ensure that `onscreen_native->gbm.next_fb`
is only assigned once per frame.
---
src/backends/native/meta-onscreen-native.c | 193 +++++++++------------
1 file changed, 86 insertions(+), 107 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 3eb5503193..c41735d90b 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -67,8 +67,6 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
struct {
struct gbm_surface *surface;
- MetaDrmBuffer *current_fb;
- MetaDrmBuffer *next_fb;
} gbm;
struct {
@@ -117,41 +115,12 @@ init_secondary_gpu_state (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
GError **error);
-static void
-swap_secondary_drm_fb (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
-
- secondary_gpu_state = onscreen_native->secondary_gpu_state;
- if (!secondary_gpu_state)
- return;
-
- g_set_object (&secondary_gpu_state->gbm.current_fb,
- secondary_gpu_state->gbm.next_fb);
- g_clear_object (&secondary_gpu_state->gbm.next_fb);
-}
-
-static void
-free_current_secondary_bo (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
-
- secondary_gpu_state = onscreen_native->secondary_gpu_state;
- if (!secondary_gpu_state)
- return;
-
- g_clear_object (&secondary_gpu_state->gbm.current_fb);
-}
-
static void
free_current_bo (CoglOnscreen *onscreen)
{
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
g_clear_object (&onscreen_native->gbm.current_fb);
- free_current_secondary_bo (onscreen);
}
static void
@@ -166,8 +135,6 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb);
g_clear_object (&onscreen_native->gbm.next_fb);
-
- swap_secondary_drm_fb (onscreen);
}
static void
@@ -436,7 +403,6 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
MetaKmsDevice *kms_device;
MetaKms *kms;
MetaKmsUpdate *kms_update;
- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL;
MetaDrmBuffer *buffer;
MetaKmsPlaneAssignment *plane_assignment;
@@ -455,15 +421,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
- if (gpu_kms == render_gpu)
- {
- buffer = onscreen_native->gbm.next_fb;
- }
- else
- {
- secondary_gpu_state = onscreen_native->secondary_gpu_state;
- buffer = secondary_gpu_state->gbm.next_fb;
- }
+ buffer = onscreen_native->gbm.next_fb;
plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms,
buffer,
@@ -566,8 +524,6 @@ secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_sta
NULL);
}
- g_clear_object (&secondary_gpu_state->gbm.current_fb);
- g_clear_object (&secondary_gpu_state->gbm.next_fb);
g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy);
secondary_gpu_release_dumb (secondary_gpu_state);
@@ -575,11 +531,11 @@ secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_sta
g_free (secondary_gpu_state);
}
-static gboolean
+static MetaDrmBuffer *
import_shared_framebuffer (CoglOnscreen *onscreen,
- MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
+ MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
+ MetaDrmBuffer *primary_gpu_fb)
{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaRenderDevice *render_device;
g_autoptr (GError) error = NULL;
MetaDrmBuffer *imported_buffer;
@@ -587,7 +543,7 @@ import_shared_framebuffer (CoglOnscreen *onscreen,
render_device = secondary_gpu_state->renderer_gpu_data->render_device;
imported_buffer =
meta_render_device_import_dma_buf (render_device,
- onscreen_native->gbm.next_fb,
+ primary_gpu_fb,
&error);
if (!imported_buffer)
{
@@ -601,16 +557,9 @@ import_shared_framebuffer (CoglOnscreen *onscreen,
META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE);
secondary_gpu_state->import_status =
META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED;
- return FALSE;
+ return NULL;
}
- /*
- * next_fb may already contain a fallback buffer, so clear it only
- * when we are sure to succeed.
- */
- g_clear_object (&secondary_gpu_state->gbm.next_fb);
- secondary_gpu_state->gbm.next_fb = imported_buffer;
-
if (secondary_gpu_state->import_status ==
META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE)
{
@@ -627,16 +576,16 @@ import_shared_framebuffer (CoglOnscreen *onscreen,
secondary_gpu_state->import_status =
META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK;
- return TRUE;
+ return imported_buffer;
}
-static void
+static MetaDrmBuffer *
copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
MetaRendererNativeGpuData *renderer_gpu_data,
- gboolean *egl_context_changed)
+ gboolean *egl_context_changed,
+ MetaDrmBuffer *primary_gpu_fb)
{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
MetaGles3 *gles3 = meta_renderer_native_get_gles3 (renderer_native);
@@ -652,9 +601,6 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu,
"FB Copy (secondary GPU)");
- g_warn_if_fail (secondary_gpu_state->gbm.next_fb == NULL);
- g_clear_object (&secondary_gpu_state->gbm.next_fb);
-
render_device = renderer_gpu_data->render_device;
egl_display = meta_render_device_get_egl_display (render_device);
@@ -667,13 +613,13 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
{
g_warning ("Failed to make current: %s", error->message);
g_error_free (error);
- return;
+ return NULL;
}
*egl_context_changed = TRUE;
- buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb);
+ buffer_gbm = META_DRM_BUFFER_GBM (primary_gpu_fb);
bo = meta_drm_buffer_gbm_get_bo (buffer_gbm);
if (!meta_renderer_native_gles3_blit_shared_bo (egl,
gles3,
@@ -685,7 +631,7 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
{
g_warning ("Failed to blit shared framebuffer: %s", error->message);
g_error_free (error);
- return;
+ return NULL;
}
if (!meta_egl_swap_buffers (egl,
@@ -695,7 +641,7 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
{
g_warning ("Failed to swap buffers: %s", error->message);
g_error_free (error);
- return;
+ return NULL;
}
use_modifiers = meta_renderer_native_use_modifiers (renderer_native);
@@ -715,10 +661,10 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
g_warning ("meta_drm_buffer_gbm_new_lock_front failed: %s",
error->message);
g_error_free (error);
- return;
+ return NULL;
}
- secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_gbm);
+ return META_DRM_BUFFER (buffer_gbm);
}
static MetaDrmBufferDumb *
@@ -733,7 +679,7 @@ secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *seconda
return secondary_gpu_state->cpu.dumb_fbs[0];
}
-static gboolean
+static MetaDrmBuffer *
copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscreen,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
const int *rectangles,
@@ -761,7 +707,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre
primary_gpu_data =
meta_renderer_native_get_gpu_data (renderer_native, primary_gpu);
if (!primary_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers)
- return FALSE;
+ return NULL;
buffer_dumb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state);
buffer = META_DRM_BUFFER (buffer_dumb);
@@ -784,7 +730,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre
{
meta_topic (META_DEBUG_KMS,
"Failed to create DMA buffer: %s", error->message);
- return FALSE;
+ return NULL;
}
dmabuf_fb =
@@ -802,7 +748,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre
meta_topic (META_DEBUG_KMS,
"Failed to create DMA buffer for blitting: %s",
error->message);
- return FALSE;
+ return NULL;
}
/* Limit the number of individual copies to 16 */
#define MAX_RECTS 16
@@ -815,7 +761,7 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre
&error))
{
g_object_unref (dmabuf_fb);
- return FALSE;
+ return NULL;
}
}
else
@@ -832,20 +778,19 @@ copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscre
&error))
{
g_object_unref (dmabuf_fb);
- return FALSE;
+ return NULL;
}
}
}
g_object_unref (dmabuf_fb);
- g_set_object (&secondary_gpu_state->gbm.next_fb, buffer);
secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb;
- return TRUE;
+ return g_object_ref (buffer);
}
-static void
+static MetaDrmBuffer *
copy_shared_framebuffer_cpu (CoglOnscreen *onscreen,
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
MetaRendererNativeGpuData *renderer_gpu_data)
@@ -897,17 +842,19 @@ copy_shared_framebuffer_cpu (CoglOnscreen *onscreen,
cogl_object_unref (dumb_bitmap);
- g_set_object (&secondary_gpu_state->gbm.next_fb, buffer);
secondary_gpu_state->cpu.current_dumb_fb = buffer_dumb;
+
+ return g_object_ref (buffer);
}
-static void
+static MetaDrmBuffer *
update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen,
const int *rectangles,
int n_rectangles)
{
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+ MetaDrmBuffer *copy = NULL;
COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePreSwapBuffers,
"Onscreen (secondary gpu pre-swap-buffers)");
@@ -933,10 +880,11 @@ update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen,
/* prepare fallback */
G_GNUC_FALLTHROUGH;
case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY:
- if (!copy_shared_framebuffer_primary_gpu (onscreen,
- secondary_gpu_state,
- rectangles,
- n_rectangles))
+ copy = copy_shared_framebuffer_primary_gpu (onscreen,
+ secondary_gpu_state,
+ rectangles,
+ n_rectangles);
+ if (!copy)
{
if (!secondary_gpu_state->noted_primary_gpu_copy_failed)
{
@@ -946,9 +894,9 @@ update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen,
secondary_gpu_state->noted_primary_gpu_copy_failed = TRUE;
}
- copy_shared_framebuffer_cpu (onscreen,
- secondary_gpu_state,
- renderer_gpu_data);
+ copy = copy_shared_framebuffer_cpu (onscreen,
+ secondary_gpu_state,
+ renderer_gpu_data);
}
else if (!secondary_gpu_state->noted_primary_gpu_copy_ok)
{
@@ -960,11 +908,15 @@ update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen,
break;
}
}
+
+ return copy;
}
static void
-update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen,
- gboolean *egl_context_changed)
+update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen,
+ gboolean *egl_context_changed,
+ MetaDrmBuffer *primary_gpu_fb,
+ MetaDrmBuffer **secondary_gpu_fb)
{
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaRendererNative *renderer_native = onscreen_native->renderer_native;
@@ -977,6 +929,7 @@ update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen,
if (secondary_gpu_state)
{
MetaRendererNativeGpuData *renderer_gpu_data;
+ g_autoptr (MetaDrmBuffer) next_fb = NULL;
renderer_gpu_data =
meta_renderer_native_get_gpu_data (renderer_native,
@@ -984,23 +937,30 @@ update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen,
switch (renderer_gpu_data->secondary.copy_mode)
{
case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO:
- if (import_shared_framebuffer (onscreen, secondary_gpu_state))
+ next_fb = import_shared_framebuffer (onscreen,
+ secondary_gpu_state,
+ primary_gpu_fb);
+ if (next_fb)
break;
-
- /* The fallback was prepared in pre_swap_buffers */
+ /* The fallback was prepared in pre_swap_buffers and is currently
+ * in secondary_gpu_fb.
+ */
renderer_gpu_data->secondary.copy_mode =
META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY;
G_GNUC_FALLTHROUGH;
case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY:
- /* Done before eglSwapBuffers. */
+ next_fb = g_object_ref (*secondary_gpu_fb);
break;
case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU:
- copy_shared_framebuffer_gpu (onscreen,
- secondary_gpu_state,
- renderer_gpu_data,
- egl_context_changed);
+ next_fb = copy_shared_framebuffer_gpu (onscreen,
+ secondary_gpu_state,
+ renderer_gpu_data,
+ egl_context_changed,
+ primary_gpu_fb);
break;
}
+
+ g_set_object (secondary_gpu_fb, next_fb);
}
}
@@ -1050,6 +1010,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
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;
MetaKmsCrtc *kms_crtc;
MetaKmsDevice *kms_device;
MetaKmsUpdateFlag flags;
@@ -1059,9 +1021,10 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers,
"Onscreen (swap-buffers)");
- update_secondary_gpu_state_pre_swap_buffers (onscreen,
- rectangles,
- n_rectangles);
+ secondary_gpu_fb =
+ update_secondary_gpu_state_pre_swap_buffers (onscreen,
+ rectangles,
+ n_rectangles);
parent_class = COGL_ONSCREEN_CLASS (meta_onscreen_native_parent_class);
parent_class->swap_buffers_with_damage (onscreen,
@@ -1077,9 +1040,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
- g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
- g_clear_object (&onscreen_native->gbm.next_fb);
-
buffer_flags = META_DRM_BUFFER_FLAG_NONE;
if (!meta_renderer_native_use_modifiers (renderer_native))
buffer_flags |= META_DRM_BUFFER_FLAG_DISABLE_MODIFIERS;
@@ -1097,8 +1057,7 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
return;
}
- onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm);
-
+ primary_gpu_fb = META_DRM_BUFFER (g_steal_pointer (&buffer_gbm));
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
@@ -1109,7 +1068,27 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
#endif
}
- update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
+ update_secondary_gpu_state_post_swap_buffers (onscreen,
+ &egl_context_changed,
+ primary_gpu_fb,
+ &secondary_gpu_fb);
+
+ switch (renderer_gpu_data->mode)
+ {
+ case META_RENDERER_NATIVE_MODE_GBM:
+ g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
+ if (onscreen_native->secondary_gpu_state)
+ g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb);
+ else
+ g_set_object (&onscreen_native->gbm.next_fb, primary_gpu_fb);
+ break;
+ case META_RENDERER_NATIVE_MODE_SURFACELESS:
+ break;
+#ifdef HAVE_EGL_DEVICE
+ case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
+ break;
+#endif
+ }
/*
* If we changed EGL context, cogl will have the wrong idea about what is
--
GitLab
From 0c766c2cd903fc084dd186b4a30053ac3ca054d6 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 19 Jan 2022 18:43:15 +0800
Subject: [PATCH 02/44] onscreen/native: Hold primary GPU FBs for the secondary
scanout lifetime
This is out of an abundance of caution. In cases where we have copied the
primary GPU's buffer to a dumb buffer for secondary GPU (DisplayLink)
scanout, we don't really know how long OpenGL will take to complete that
copy in the background. And there is no swap buffers or locking of the
secondary GPU front buffer to guarantee when.
So to avoid needing `glFinish` or similar we now reference the original
source FB for the scanout lifetime of the secondary (dumb) buffer. This
is effectively identical to the pre-!2087 behaviour.
Although it's not theoretically optimal because the primary GPU FB is
not needed after scanout starts (`next_fb` is moved to `current_fb`),
this approach does give us the simplicity of only having to track the
lifetime of a single buffer after it is flipped.
---
src/backends/native/meta-onscreen-native.c | 64 +++++++++++++++++++++-
1 file changed, 61 insertions(+), 3 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index c41735d90b..55dbcfef7b 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -72,6 +72,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState
struct {
MetaDrmBufferDumb *current_dumb_fb;
MetaDrmBufferDumb *dumb_fbs[2];
+ MetaDrmBuffer *source_fbs[2];
} cpu;
gboolean noted_primary_gpu_copy_ok;
@@ -493,13 +494,41 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen,
kms_update);
}
+static void
+hold_primary_gpu_fb_for_secondary_gpu_scanout (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
+ MetaDrmBuffer *primary_gpu_fb,
+ MetaDrmBuffer *secondary_gpu_fb)
+{
+ if (META_IS_DRM_BUFFER_DUMB (secondary_gpu_fb))
+ {
+ MetaDrmBufferDumb *dumb_fb = META_DRM_BUFFER_DUMB (secondary_gpu_fb);
+ int i;
+ const int n = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs);
+
+ for (i = 0; i < n; i++)
+ {
+ if (dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i])
+ {
+ g_set_object (&secondary_gpu_state->cpu.source_fbs[i],
+ primary_gpu_fb);
+ break;
+ }
+ }
+
+ g_warn_if_fail (i < n);
+ }
+}
+
static void
secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
{
unsigned i;
for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
- g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]);
+ {
+ g_clear_object (&secondary_gpu_state->cpu.dumb_fbs[i]);
+ g_clear_object (&secondary_gpu_state->cpu.source_fbs[i]);
+ }
}
static void
@@ -1078,9 +1107,17 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
case META_RENDERER_NATIVE_MODE_GBM:
g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
if (onscreen_native->secondary_gpu_state)
- g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb);
+ {
+ g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb);
+ hold_primary_gpu_fb_for_secondary_gpu_scanout (
+ onscreen_native->secondary_gpu_state,
+ primary_gpu_fb,
+ secondary_gpu_fb);
+ }
else
- g_set_object (&onscreen_native->gbm.next_fb, primary_gpu_fb);
+ {
+ g_set_object (&onscreen_native->gbm.next_fb, primary_gpu_fb);
+ }
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
break;
@@ -1960,6 +1997,21 @@ pick_secondary_gpu_framebuffer_format_for_cpu (CoglOnscreen *onscreen)
return DRM_FORMAT_INVALID;
}
+static void
+dumb_toggle_notify (gpointer data,
+ GObject *object,
+ gboolean is_last_ref)
+{
+ MetaDrmBuffer **source_fb = data;
+
+ g_return_if_fail (source_fb != NULL);
+ if (is_last_ref && *source_fb)
+ {
+ g_return_if_fail (META_IS_DRM_BUFFER (*source_fb));
+ g_clear_object (source_fb);
+ }
+}
+
static gboolean
init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
@@ -2016,6 +2068,12 @@ init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_nat
}
secondary_gpu_state->cpu.dumb_fbs[i] = META_DRM_BUFFER_DUMB (dumb_buffer);
+ g_object_add_toggle_ref (G_OBJECT (dumb_buffer),
+ dumb_toggle_notify,
+ &secondary_gpu_state->cpu.source_fbs[i]);
+
+ /* It was incremented higher than we need by add_toggle_ref */
+ g_object_unref (dumb_buffer);
}
/*
--
GitLab
From 0a839e19f861390be42f10dc184f187bb163c8e2 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Mon, 16 Aug 2021 17:06:43 +0800
Subject: [PATCH 03/44] kms/update: Hold a reference to the buffer used in a
plane assignment
---
src/backends/native/meta-kms-update.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c
index 53fc92eb86..2889338ec5 100644
--- a/src/backends/native/meta-kms-update.c
+++ b/src/backends/native/meta-kms-update.c
@@ -149,6 +149,7 @@ static void
meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment)
{
g_clear_pointer (&plane_assignment->fb_damage, meta_kms_fb_damage_free);
+ g_clear_object (&plane_assignment->buffer);
g_free (plane_assignment);
}
@@ -228,7 +229,7 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update,
.update = update,
.crtc = crtc,
.plane = plane,
- .buffer = buffer,
+ .buffer = g_object_ref (buffer),
.src_rect = src_rect,
.dst_rect = dst_rect,
.flags = flags,
--
GitLab
From 4a47a2221ef11c8e88d652860c189565536b7422 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Mon, 16 Aug 2021 18:04:10 +0800
Subject: [PATCH 04/44] kms/crtc: Add an API to hold references to buffers
while asssigned to planes
---
src/backends/native/meta-kms-crtc.c | 80 +++++++++++++++++++++++++++++
src/backends/native/meta-kms-crtc.h | 9 ++++
2 files changed, 89 insertions(+)
diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c
index 685a3737ca..45c7eb78ee 100644
--- a/src/backends/native/meta-kms-crtc.c
+++ b/src/backends/native/meta-kms-crtc.c
@@ -32,6 +32,12 @@ typedef struct _MetaKmsCrtcPropTable
MetaKmsProp props[META_KMS_CRTC_N_PROPS];
} MetaKmsCrtcPropTable;
+typedef struct
+{
+ MetaDrmBuffer *front, *back;
+ gboolean back_is_set;
+} PlaneState;
+
struct _MetaKmsCrtc
{
GObject parent;
@@ -44,6 +50,8 @@ struct _MetaKmsCrtc
MetaKmsCrtcState current_state;
MetaKmsCrtcPropTable prop_table;
+
+ GHashTable *plane_states;
};
G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT)
@@ -441,20 +449,91 @@ meta_kms_crtc_new (MetaKmsImplDevice *impl_device,
return crtc;
}
+void
+meta_kms_crtc_remember_plane_buffer (MetaKmsCrtc *crtc,
+ uint32_t plane_id,
+ MetaDrmBuffer *buffer)
+{
+ gpointer key = GUINT_TO_POINTER (plane_id);
+ PlaneState *plane_state;
+
+ plane_state = g_hash_table_lookup (crtc->plane_states, key);
+ if (plane_state == NULL)
+ {
+ plane_state = g_new0 (PlaneState, 1);
+ g_hash_table_insert (crtc->plane_states, key, plane_state);
+ }
+
+ plane_state->back_is_set = TRUE; /* note buffer may be NULL */
+ g_set_object (&plane_state->back, buffer);
+}
+
+static void
+swap_plane_buffers (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ PlaneState *plane_state = value;
+
+ if (plane_state->back_is_set)
+ {
+ g_set_object (&plane_state->front, plane_state->back);
+ g_clear_object (&plane_state->back);
+ plane_state->back_is_set = FALSE;
+ }
+}
+
+void
+meta_kms_crtc_on_scanout_started (MetaKmsCrtc *crtc)
+{
+ g_hash_table_foreach (crtc->plane_states, swap_plane_buffers, NULL);
+}
+
+void
+meta_kms_crtc_release_buffers (MetaKmsCrtc *crtc)
+{
+ g_hash_table_remove_all (crtc->plane_states);
+}
+
+static void
+meta_kms_crtc_dispose (GObject *object)
+{
+ MetaKmsCrtc *crtc = META_KMS_CRTC (object);
+
+ meta_kms_crtc_release_buffers (crtc);
+
+ G_OBJECT_CLASS (meta_kms_crtc_parent_class)->dispose (object);
+}
+
static void
meta_kms_crtc_finalize (GObject *object)
{
MetaKmsCrtc *crtc = META_KMS_CRTC (object);
clear_gamma_state (&crtc->current_state);
+ g_hash_table_unref (crtc->plane_states);
G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object);
}
+static void
+destroy_plane_state (gpointer data)
+{
+ PlaneState *plane_state = data;
+
+ g_clear_object (&plane_state->front);
+ g_clear_object (&plane_state->back);
+ g_free (plane_state);
+}
+
static void
meta_kms_crtc_init (MetaKmsCrtc *crtc)
{
crtc->current_state.gamma.size = 0;
+ crtc->plane_states = g_hash_table_new_full (NULL,
+ NULL,
+ NULL,
+ destroy_plane_state);
}
static void
@@ -462,5 +541,6 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = meta_kms_crtc_dispose;
object_class->finalize = meta_kms_crtc_finalize;
}
diff --git a/src/backends/native/meta-kms-crtc.h b/src/backends/native/meta-kms-crtc.h
index 218bec9a15..29fdfcb5dd 100644
--- a/src/backends/native/meta-kms-crtc.h
+++ b/src/backends/native/meta-kms-crtc.h
@@ -25,6 +25,7 @@
#include <xf86drmMode.h>
#include "backends/native/meta-kms-types.h"
+#include "backends/native/meta-drm-buffer.h"
#include "core/util-private.h"
#include "meta/boxes.h"
@@ -82,4 +83,12 @@ MetaKmsCrtcGamma * meta_kms_crtc_gamma_new (MetaKmsCrtc *crtc,
const uint16_t *green,
const uint16_t *blue);
+void meta_kms_crtc_remember_plane_buffer (MetaKmsCrtc *crtc,
+ uint32_t plane_id,
+ MetaDrmBuffer *buffer);
+
+void meta_kms_crtc_on_scanout_started (MetaKmsCrtc *crtc);
+
+void meta_kms_crtc_release_buffers (MetaKmsCrtc *crtc);
+
#endif /* META_KMS_CRTC_H */
--
GitLab
From f9c6c62942cd3f7a849338b1b325f15d47619d7f Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 17 Aug 2021 16:35:18 +0800
Subject: [PATCH 05/44] kms/page-flip: Pump the CRTC's buffer reference queue
on scanout
Precise timing of this call is not important. We only need to notify
that the *previous* scanout is definitely no longer happening. So any
old buffers no longer used in the latest scanout can be released.
---
src/backends/native/meta-kms-page-flip.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c
index 817f4e7c8d..148d237400 100644
--- a/src/backends/native/meta-kms-page-flip.c
+++ b/src/backends/native/meta-kms-page-flip.c
@@ -24,6 +24,7 @@
#include "backends/native/meta-kms-impl.h"
#include "backends/native/meta-kms-private.h"
#include "backends/native/meta-kms-update.h"
+#include "backends/native/meta-kms-crtc.h"
typedef struct _MetaKmsPageFlipClosure
{
@@ -149,6 +150,8 @@ meta_kms_page_flip_data_flipped (MetaKms *kms,
meta_assert_not_in_kms_impl (kms);
+ meta_kms_crtc_on_scanout_started (page_flip_data->crtc);
+
for (l = page_flip_data->closures; l; l = l->next)
{
MetaKmsPageFlipClosure *closure = l->data;
--
GitLab
From 10f25bb169e66ca36c63be37aa859514295adc1b Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 9 Feb 2022 16:44:09 +0800
Subject: [PATCH 06/44] kms/impl-device: Release buffers from MetaKmsCrtc in
prepare_shutdown
This allows them to release their buffers cleanly while all the
associated resources to do the release still exist. Otherwise we
might crash later in meta_drm_buffer_finalize.
---
src/backends/native/meta-kms-impl-device.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c
index b05bf5fdab..1177707fae 100644
--- a/src/backends/native/meta-kms-impl-device.c
+++ b/src/backends/native/meta-kms-impl-device.c
@@ -1022,8 +1022,12 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device,
void
meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device)
{
+ MetaKmsImplDevicePrivate *priv =
+ meta_kms_impl_device_get_instance_private (impl_device);
MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
+ g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_release_buffers, NULL);
+
if (klass->prepare_shutdown)
klass->prepare_shutdown (impl_device);
--
GitLab
From 5337749757f0873603c05ef7d361e15007d256e5 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 7 Dec 2021 18:31:16 +0800
Subject: [PATCH 07/44] kms/impl-device/simple: Pump the CRTC's buffer
reference queue on scanout
In this case "on scanout" means when `drmModeSetCrtc` succeeds. This is
the only case where there won't be a `meta_kms_page_flip_data_flipped` to
do it for us.
---
src/backends/native/meta-kms-impl-device-simple.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c
index 882cd97cc9..6f9dfee7ea 100644
--- a/src/backends/native/meta-kms-impl-device-simple.c
+++ b/src/backends/native/meta-kms-impl-device-simple.c
@@ -470,6 +470,8 @@ process_mode_set (MetaKmsImplDevice *impl_device,
return FALSE;
}
+ meta_kms_crtc_on_scanout_started (crtc);
+
if (drm_mode)
{
g_hash_table_replace (impl_device_simple->cached_mode_sets,
@@ -859,6 +861,8 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple,
return FALSE;
}
+ meta_kms_crtc_on_scanout_started (crtc);
+
if (!impl_device_simple->mode_set_fallback_feedback_source)
{
GSource *source;
--
GitLab
From 40b469b97e30923453c2293549efd86eb9ffa5eb Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 17 Aug 2021 17:40:57 +0800
Subject: [PATCH 08/44] kms/impl-device/simple: Keep a reference to buffers
assigned to planes
The MetaKmsCrtc will hold these references during future scanouts until
such time as a new buffer is assigned to the plane AND another scanout
has occurred after that.
---
src/backends/native/meta-kms-impl-device-simple.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c
index 6f9dfee7ea..c1e2707537 100644
--- a/src/backends/native/meta-kms-impl-device-simple.c
+++ b/src/backends/native/meta-kms-impl-device-simple.c
@@ -1294,7 +1294,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
{
case META_KMS_PLANE_TYPE_PRIMARY:
/* Handled as part of the mode-set and page flip. */
- return TRUE;
+ goto assigned;
case META_KMS_PLANE_TYPE_CURSOR:
if (!process_cursor_plane_assignment (impl_device, update,
plane_assignment,
@@ -1308,7 +1308,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
}
else
{
- return TRUE;
+ goto assigned;
}
case META_KMS_PLANE_TYPE_OVERLAY:
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -1321,6 +1321,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
}
g_assert_not_reached ();
+
+assigned:
+ meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc,
+ meta_kms_plane_get_id (plane),
+ plane_assignment->buffer);
+ return TRUE;
}
static gboolean
--
GitLab
From 2d8ac9727a89aac439c14d965a158f208571303e Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 7 Dec 2021 17:54:15 +0800
Subject: [PATCH 09/44] kms/impl-device/simple: Keep a reference to buffers
needed in retries
---
.../native/meta-kms-impl-device-simple.c | 24 ++++++++++++-------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c
index c1e2707537..8aa78343a7 100644
--- a/src/backends/native/meta-kms-impl-device-simple.c
+++ b/src/backends/native/meta-kms-impl-device-simple.c
@@ -536,7 +536,7 @@ is_timestamp_earlier_than (uint64_t ts1,
typedef struct _RetryPageFlipData
{
MetaKmsCrtc *crtc;
- uint32_t fb_id;
+ MetaDrmBuffer *fb;
MetaKmsPageFlipData *page_flip_data;
float refresh_rate;
uint64_t retry_time_us;
@@ -549,6 +549,7 @@ retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
g_assert (!retry_page_flip_data->page_flip_data);
g_clear_pointer (&retry_page_flip_data->custom_page_flip,
meta_kms_custom_page_flip_free);
+ g_clear_object (&retry_page_flip_data->fb);
g_free (retry_page_flip_data);
}
@@ -616,16 +617,21 @@ retry_page_flips (gpointer user_data)
}
else
{
+ uint32_t fb_id =
+ retry_page_flip_data->fb ?
+ meta_drm_buffer_get_fb_id (retry_page_flip_data->fb) :
+ 0;
+
meta_topic (META_DEBUG_KMS,
"[simple] Retrying page flip on CRTC %u (%s) with %u",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device),
- retry_page_flip_data->fb_id);
+ fb_id);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
- retry_page_flip_data->fb_id,
+ fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
retry_page_flip_data->page_flip_data);
}
@@ -712,7 +718,7 @@ retry_page_flips (gpointer user_data)
static void
schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple,
MetaKmsCrtc *crtc,
- uint32_t fb_id,
+ MetaDrmBuffer *fb,
float refresh_rate,
MetaKmsPageFlipData *page_flip_data,
MetaKmsCustomPageFlip *custom_page_flip)
@@ -727,7 +733,7 @@ schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple,
retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
*retry_page_flip_data = (RetryPageFlipData) {
.crtc = crtc,
- .fb_id = fb_id,
+ .fb = fb ? g_object_ref (fb) : NULL,
.page_flip_data = page_flip_data,
.refresh_rate = refresh_rate,
.retry_time_us = retry_time_us,
@@ -987,20 +993,20 @@ dispatch_page_flip (MetaKmsImplDevice *impl_device,
cached_mode_set = get_cached_mode_set (impl_device_simple, crtc);
if (cached_mode_set)
{
- uint32_t fb_id;
+ MetaDrmBuffer *fb;
drmModeModeInfo *drm_mode;
float refresh_rate;
if (plane_assignment)
- fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer);
+ fb = plane_assignment->buffer;
else
- fb_id = 0;
+ fb = NULL;
drm_mode = cached_mode_set->drm_mode;
refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
meta_kms_impl_device_hold_fd (impl_device);
schedule_retry_page_flip (impl_device_simple,
crtc,
- fb_id,
+ fb,
refresh_rate,
page_flip_data,
g_steal_pointer (&custom_page_flip));
--
GitLab
From 84e908dc6707efd1befa1132d3264a8c28bec43f Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 17 Aug 2021 16:36:25 +0800
Subject: [PATCH 10/44] kms/impl-device/atomic: Keep a reference to buffers
assigned to planes
The MetaKmsCrtc will hold these references during future scanouts until
such time as a new buffer is assigned to the plane AND another scanout
has occurred after that.
---
src/backends/native/meta-kms-impl-device-atomic.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c
index 73dd8e6971..787d05acda 100644
--- a/src/backends/native/meta-kms-impl-device-atomic.c
+++ b/src/backends/native/meta-kms-impl-device-atomic.c
@@ -431,6 +431,7 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
{
MetaKmsPlaneAssignment *plane_assignment = update_entry;
MetaKmsPlane *plane = plane_assignment->plane;
+ MetaKmsUpdateFlag flags = (MetaKmsUpdateFlag) user_data;
MetaDrmBuffer *buffer;
MetaKmsFbDamage *fb_damage;
uint32_t prop_id;
@@ -586,6 +587,12 @@ process_plane_assignment (MetaKmsImplDevice *impl_device,
error))
return FALSE;
}
+
+ if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY))
+ meta_kms_crtc_remember_plane_buffer (plane_assignment->crtc,
+ meta_kms_plane_get_id (plane),
+ buffer);
+
return TRUE;
}
@@ -963,7 +970,7 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device,
req,
blob_ids,
meta_kms_update_get_plane_assignments (update),
- NULL,
+ GUINT_TO_POINTER (flags),
process_plane_assignment,
&error))
goto err;
--
GitLab
From 7891754a97fcb96f6d3b4103fd4d4482db10f70f Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 18 Aug 2021 15:04:27 +0800
Subject: [PATCH 11/44] onscreen/native: Stop holding the current onscreen
buffer
The scanout lifetime of the buffer is now automatically managed via
meta_crtc_kms_assign_primary_plane. Se we don't need `current_fb` at
all, and `next_fb` can be unreferenced as soon as we've passed it to
meta_crtc_kms_assign_primary_plane.
---
src/backends/native/meta-onscreen-native.c | 34 +++-------------------
1 file changed, 4 insertions(+), 30 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 55dbcfef7b..ac073901c4 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -93,7 +93,6 @@ struct _MetaOnscreenNative
struct {
struct gbm_surface *surface;
- MetaDrmBuffer *current_fb;
MetaDrmBuffer *next_fb;
} gbm;
@@ -116,28 +115,6 @@ init_secondary_gpu_state (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
GError **error);
-static void
-free_current_bo (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
-
- g_clear_object (&onscreen_native->gbm.current_fb);
-}
-
-static void
-meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
-{
- MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
-
- if (!onscreen_native->gbm.next_fb)
- return;
-
- free_current_bo (onscreen);
-
- g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb);
- g_clear_object (&onscreen_native->gbm.next_fb);
-}
-
static void
maybe_update_frame_info (MetaCrtc *crtc,
CoglFrameInfo *frame_info,
@@ -202,7 +179,6 @@ notify_view_crtc_presented (MetaRendererView *view,
maybe_update_frame_info (crtc, frame_info, time_us, flags, sequence);
meta_onscreen_native_notify_frame_complete (onscreen);
- meta_onscreen_native_swap_drm_fb (onscreen);
}
static int64_t
@@ -313,7 +289,6 @@ 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_swap_drm_fb (onscreen);
}
static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = {
@@ -378,8 +353,9 @@ void
meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen)
{
CoglFrameInfo *frame_info;
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
- meta_onscreen_native_swap_drm_fb (onscreen);
+ g_clear_object (&onscreen_native->gbm.next_fb);
frame_info = cogl_onscreen_peek_tail_frame_info (onscreen);
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
@@ -404,7 +380,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
MetaKmsDevice *kms_device;
MetaKms *kms;
MetaKmsUpdate *kms_update;
- MetaDrmBuffer *buffer;
+ g_autoptr (MetaDrmBuffer) buffer = NULL;
MetaKmsPlaneAssignment *plane_assignment;
COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs,
@@ -422,7 +398,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
- buffer = onscreen_native->gbm.next_fb;
+ buffer = g_steal_pointer (&onscreen_native->gbm.next_fb);
plane_assignment = meta_crtc_kms_assign_primary_plane (crtc_kms,
buffer,
@@ -1374,7 +1350,6 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
break;
- g_clear_object (&onscreen_native->gbm.next_fb);
g_propagate_error (error, g_error_copy (feedback_error));
return FALSE;
}
@@ -2174,7 +2149,6 @@ meta_onscreen_native_dispose (GObject *object)
{
case META_RENDERER_NATIVE_MODE_GBM:
g_clear_object (&onscreen_native->gbm.next_fb);
- free_current_bo (onscreen);
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
--
GitLab
From 210e53d132a3a494d70533c1a6c6b4839bd3661a Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Fri, 24 Sep 2021 21:49:45 +0800
Subject: [PATCH 12/44] onscreen/native: Keep the onscreen alive longer than
`MetaDrmBufferGbm`
Because `MetaDrmBufferGbm` uses `gbm_surface` in its destructor and
`gbm_surface` can't be refcounted directly. So we reference its owner
(the onscreen) instead.
This avoids a crash that can otherwise occur when hotplugging monitors as
the old onscreen might get disposed before the new onscreen has presented
a replacement frame on the same `MetaKmsCrtc`.
We could instead wrap and refcount all users of `gbm_surface`, but that
would be a lot more work for no additional benefit right now.
---
src/backends/native/meta-onscreen-native.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index ac073901c4..8d36eb10c8 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1063,6 +1063,12 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
}
primary_gpu_fb = META_DRM_BUFFER (g_steal_pointer (&buffer_gbm));
+
+ g_object_set_data_full (G_OBJECT (primary_gpu_fb),
+ "gbm_surface owner",
+ g_object_ref (onscreen),
+ (GDestroyNotify) g_object_unref);
+
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
--
GitLab
From aa7b39fa8775db7c1bae839a38047bb816d572b0 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 18 Aug 2021 15:59:16 +0800
Subject: [PATCH 13/44] cursor-renderer/native: Stop holding onto historical
cursor buffers
Their scanout lifetimes are now managed automatically via the call
to meta_kms_update_assign_plane.
---
.../native/meta-cursor-renderer-native.c | 83 ++++---------------
1 file changed, 14 insertions(+), 69 deletions(-)
diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
index df5862c4c0..b27afc8e6d 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -64,19 +64,6 @@
#define DRM_CAP_CURSOR_HEIGHT 0x9
#endif
-/* When animating a cursor, we usually call drmModeSetCursor2 once per frame.
- * Though, testing shows that we need to triple buffer the cursor buffer in
- * order to avoid glitches when animating the cursor, at least when running on
- * Intel. The reason for this might be (but is not confirmed to be) due to
- * the user space gbm_bo cache, making us reuse and overwrite the kernel side
- * buffer content before it was scanned out. To avoid this, we keep a user space
- * reference to each buffer we set until at least one frame after it was drawn.
- * In effect, this means we three active cursor gbm_bo's: one that that just has
- * been set, one that was previously set and may or may not have been scanned
- * out, and one pending that will be replaced if the cursor sprite changes.
- */
-#define HW_CURSOR_BUFFER_COUNT 3
-
static GQuark quark_cursor_sprite = 0;
typedef struct _CrtcCursorData
@@ -120,9 +107,8 @@ typedef enum _MetaCursorBufferState
typedef struct _MetaCursorNativeGpuState
{
MetaGpu *gpu;
- unsigned int active_buffer_idx;
MetaCursorBufferState pending_buffer_state;
- MetaDrmBuffer *buffers[HW_CURSOR_BUFFER_COUNT];
+ MetaDrmBuffer *buffer;
} MetaCursorNativeGpuState;
typedef struct _MetaCursorNativePrivate
@@ -199,43 +185,17 @@ meta_cursor_renderer_native_finalize (GObject *object)
G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
}
-static unsigned int
-get_pending_cursor_sprite_buffer_index (MetaCursorNativeGpuState *cursor_gpu_state)
-{
- return (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT;
-}
-
-static MetaDrmBuffer *
-get_pending_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state)
-{
- unsigned int pending_buffer_idx;
-
- pending_buffer_idx =
- get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
- return cursor_gpu_state->buffers[pending_buffer_idx];
-}
-
-static MetaDrmBuffer *
-get_active_cursor_sprite_buffer (MetaCursorNativeGpuState *cursor_gpu_state)
-{
- return cursor_gpu_state->buffers[cursor_gpu_state->active_buffer_idx];
-}
-
static void
-set_pending_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
- MetaGpuKms *gpu_kms,
- MetaDrmBuffer *buffer)
+set_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
+ MetaGpuKms *gpu_kms,
+ MetaDrmBuffer *buffer)
{
MetaCursorNativePrivate *cursor_priv;
MetaCursorNativeGpuState *cursor_gpu_state;
- unsigned int pending_buffer_idx;
cursor_priv = ensure_cursor_priv (cursor_sprite);
cursor_gpu_state = ensure_cursor_gpu_state (cursor_priv, gpu_kms);
-
- pending_buffer_idx =
- get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
- cursor_gpu_state->buffers[pending_buffer_idx] = buffer;
+ cursor_gpu_state->buffer = buffer;
cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_SET;
}
@@ -311,10 +271,7 @@ assign_cursor_plane (MetaCursorRendererNative *native,
MetaKmsUpdate *kms_update;
MetaKmsPlaneAssignment *plane_assignment;
- if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET)
- buffer = get_pending_cursor_sprite_buffer (cursor_gpu_state);
- else
- buffer = get_active_cursor_sprite_buffer (cursor_gpu_state);
+ buffer = cursor_gpu_state->buffer;
kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
kms_device = meta_kms_crtc_get_device (kms_crtc);
@@ -365,13 +322,6 @@ assign_cursor_plane (MetaCursorRendererNative *native,
native);
crtc_cursor_data->buffer = buffer;
-
- if (cursor_gpu_state->pending_buffer_state == META_CURSOR_BUFFER_STATE_SET)
- {
- cursor_gpu_state->active_buffer_idx =
- (cursor_gpu_state->active_buffer_idx + 1) % HW_CURSOR_BUFFER_COUNT;
- cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_NONE;
- }
}
static float
@@ -602,7 +552,7 @@ has_valid_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
switch (cursor_gpu_state->pending_buffer_state)
{
case META_CURSOR_BUFFER_STATE_NONE:
- return get_active_cursor_sprite_buffer (cursor_gpu_state) != NULL;
+ return cursor_gpu_state->buffer != NULL;
case META_CURSOR_BUFFER_STATE_SET:
return TRUE;
case META_CURSOR_BUFFER_STATE_INVALIDATED:
@@ -1115,16 +1065,14 @@ unset_crtc_cursor_renderer_privates (MetaGpu *gpu,
static void
cursor_gpu_state_free (MetaCursorNativeGpuState *cursor_gpu_state)
{
- int i;
MetaDrmBuffer *active_buffer;
- active_buffer = get_active_cursor_sprite_buffer (cursor_gpu_state);
+ active_buffer = cursor_gpu_state->buffer;
if (active_buffer)
unset_crtc_cursor_renderer_privates (cursor_gpu_state->gpu,
active_buffer);
- for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++)
- g_clear_object (&cursor_gpu_state->buffers[i]);
+ g_clear_object (&cursor_gpu_state->buffer);
g_free (cursor_gpu_state);
}
@@ -1162,10 +1110,7 @@ invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite)
g_hash_table_iter_init (&iter, cursor_priv->gpu_states);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state))
{
- unsigned int pending_buffer_idx;
-
- pending_buffer_idx = get_pending_cursor_sprite_buffer_index (cursor_gpu_state);
- g_clear_object (&cursor_gpu_state->buffers[pending_buffer_idx]);
+ g_clear_object (&cursor_gpu_state->buffer);
cursor_gpu_state->pending_buffer_state =
META_CURSOR_BUFFER_STATE_INVALIDATED;
}
@@ -1306,8 +1251,8 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
return;
}
- set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms,
- META_DRM_BUFFER (buffer_gbm));
+ set_cursor_sprite_buffer (cursor_sprite, gpu_kms,
+ META_DRM_BUFFER (buffer_gbm));
}
else
{
@@ -1649,8 +1594,8 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
return;
}
- set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms,
- META_DRM_BUFFER (buffer_gbm));
+ set_cursor_sprite_buffer (cursor_sprite, gpu_kms,
+ META_DRM_BUFFER (buffer_gbm));
}
}
#endif
--
GitLab
From 00843efcc6f10befbefb34b2ce15a06071014212 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 18 Aug 2021 16:12:24 +0800
Subject: [PATCH 14/44] cursor-renderer/native: Remove MetaCursorBufferState
It was theoretically useful for flagging when a NULL buffer had been
SET, but in practice set_cursor_sprite_buffer is never called when the
buffer is NULL. So we can instead just compare the buffer with NULL to
tell when it is valid.
---
.../native/meta-cursor-renderer-native.c | 61 ++-----------------
1 file changed, 4 insertions(+), 57 deletions(-)
diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
index b27afc8e6d..998af173d3 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -97,17 +97,9 @@ typedef struct _MetaCursorRendererNativeGpuData
uint64_t cursor_height;
} MetaCursorRendererNativeGpuData;
-typedef enum _MetaCursorBufferState
-{
- META_CURSOR_BUFFER_STATE_NONE,
- META_CURSOR_BUFFER_STATE_SET,
- META_CURSOR_BUFFER_STATE_INVALIDATED,
-} MetaCursorBufferState;
-
typedef struct _MetaCursorNativeGpuState
{
MetaGpu *gpu;
- MetaCursorBufferState pending_buffer_state;
MetaDrmBuffer *buffer;
} MetaCursorNativeGpuState;
@@ -196,7 +188,6 @@ set_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
cursor_priv = ensure_cursor_priv (cursor_sprite);
cursor_gpu_state = ensure_cursor_gpu_state (cursor_priv, gpu_kms);
cursor_gpu_state->buffer = buffer;
- cursor_gpu_state->pending_buffer_state = META_CURSOR_BUFFER_STATE_SET;
}
static void
@@ -549,19 +540,7 @@ has_valid_cursor_sprite_buffer (MetaCursorSprite *cursor_sprite,
if (!cursor_gpu_state)
return FALSE;
- switch (cursor_gpu_state->pending_buffer_state)
- {
- case META_CURSOR_BUFFER_STATE_NONE:
- return cursor_gpu_state->buffer != NULL;
- case META_CURSOR_BUFFER_STATE_SET:
- return TRUE;
- case META_CURSOR_BUFFER_STATE_INVALIDATED:
- return FALSE;
- }
-
- g_assert_not_reached ();
-
- return FALSE;
+ return cursor_gpu_state->buffer != NULL;
}
static void
@@ -1109,11 +1088,7 @@ invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite)
g_hash_table_iter_init (&iter, cursor_priv->gpu_states);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state))
- {
- g_clear_object (&cursor_gpu_state->buffer);
- cursor_gpu_state->pending_buffer_state =
- META_CURSOR_BUFFER_STATE_INVALIDATED;
- }
+ g_clear_object (&cursor_gpu_state->buffer);
}
static void
@@ -1260,34 +1235,6 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
}
}
-static gboolean
-is_cursor_hw_state_valid (MetaCursorSprite *cursor_sprite,
- MetaGpuKms *gpu_kms)
-{
- MetaCursorNativePrivate *cursor_priv;
- MetaCursorNativeGpuState *cursor_gpu_state;
-
- cursor_priv = get_cursor_priv (cursor_sprite);
- if (!cursor_priv)
- return FALSE;
-
- cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms);
- if (!cursor_gpu_state)
- return FALSE;
-
- switch (cursor_gpu_state->pending_buffer_state)
- {
- case META_CURSOR_BUFFER_STATE_SET:
- case META_CURSOR_BUFFER_STATE_NONE:
- return TRUE;
- case META_CURSOR_BUFFER_STATE_INVALIDATED:
- return FALSE;
- }
-
- g_assert_not_reached ();
- return FALSE;
-}
-
static gboolean
is_cursor_scale_and_transform_valid (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
@@ -1452,7 +1399,7 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer,
if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
return;
- if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) &&
+ if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) &&
is_cursor_scale_and_transform_valid (renderer, cursor_sprite))
return;
@@ -1619,7 +1566,7 @@ realize_cursor_sprite_from_xcursor_for_gpu (MetaCursorRenderer *renderer,
if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken)
return;
- if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) &&
+ if (has_valid_cursor_sprite_buffer (cursor_sprite, gpu_kms) &&
is_cursor_scale_and_transform_valid (renderer, cursor_sprite))
return;
--
GitLab
From 4ee68afa39563ad62e4dfd4fc0ebdb3996cc6872 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 15/44] cogl/onscreen: Add function
cogl_onscreen_count_pending_frames
---
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 dffe018d2e..e0215f750d 100644
--- a/cogl/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl/cogl-onscreen-private.h
@@ -97,4 +97,7 @@ 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_count_pending_frames (CoglOnscreen *onscreen);
+
#endif /* __COGL_ONSCREEN_PRIVATE_H */
diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c
index ff9e1749a7..b25b4af4ab 100644
--- a/cogl/cogl/cogl-onscreen.c
+++ b/cogl/cogl/cogl-onscreen.c
@@ -510,6 +510,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen)
return g_queue_pop_head (&priv->pending_frame_infos);
}
+unsigned int
+cogl_onscreen_count_pending_frames (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 4e3140f981374e88ede3ad25d4a14fb517786e63 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 16/44] 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 41bf04d665..4e23121d1c 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -678,6 +678,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 aa1039da9f0b5dbf87c42cbca8655025cb5bf693 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 17/44] 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 4e23121d1c..a4ef05682d 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -661,12 +661,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 9b1317cc05fc629192fe84a23b07f2652977136e 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 18/44] 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 8d36eb10c8..1a25156007 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1026,6 +1026,19 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers,
"Onscreen (swap-buffers)");
+ if (meta_is_topic_enabled (META_DEBUG_KMS))
+ {
+ unsigned int frames_pending =
+ cogl_onscreen_count_pending_frames (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 7c0f2b01e54f4cfad3813e28e0a3bce5339ca755 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 19/44] 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 1a25156007..6ff80d29c8 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -150,7 +150,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_assert (info);
_cogl_onscreen_notify_frame_sync (onscreen, info);
_cogl_onscreen_notify_complete (onscreen, info);
--
GitLab
From ab849f90f3eb16715a279abdfc65697ea3f31654 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 20/44] 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 | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 6ff80d29c8..ea13e60c90 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1122,6 +1122,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
@@ -1146,8 +1149,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;
}
@@ -1165,9 +1166,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
"Postponing primary plane composite update for CRTC %u (%s)",
meta_kms_crtc_get_id (kms_crtc),
meta_kms_device_get_path (kms_device));
-
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
else if (meta_renderer_native_has_pending_mode_set (renderer_native))
@@ -1177,8 +1175,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
meta_renderer_native_notify_mode_sets_reset (renderer_native);
meta_renderer_native_post_mode_set_updates (renderer_native);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
break;
@@ -1191,8 +1187,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
{
meta_renderer_native_notify_mode_sets_reset (renderer_native);
meta_renderer_native_post_mode_set_updates (renderer_native);
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
return;
}
break;
@@ -1210,13 +1204,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
switch (meta_kms_feedback_get_result (kms_feedback))
{
case META_KMS_FEEDBACK_PASSED:
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
break;
case META_KMS_FEEDBACK_FAILED:
- clutter_frame_set_result (frame,
- CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
-
feedback_error = meta_kms_feedback_get_error (kms_feedback);
if (!g_error_matches (feedback_error,
G_IO_ERROR,
--
GitLab
From 54a13dcb6e3f8a29752239573a8ee0a0fee14123 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 21/44] 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 | 60 +++++++++++++++++-----
1 file changed, 46 insertions(+), 14 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index ea13e60c90..1649a9383a 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -105,11 +105,19 @@ struct _MetaOnscreenNative
#endif
MetaRendererView *view;
+
+ struct {
+ int *rectangles; /* 4 x n_rectangles */
+ int n_rectangles;
+ } next_post;
};
G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native,
COGL_TYPE_ONSCREEN_EGL)
+static void
+post_latest_swap (CoglOnscreen *onscreen);
+
static gboolean
init_secondary_gpu_state (MetaRendererNative *renderer_native,
CoglOnscreen *onscreen,
@@ -999,29 +1007,18 @@ 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);
- MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
- MetaMonitorManager *monitor_manager =
- meta_backend_get_monitor_manager (backend);
- MetaKms *kms = meta_backend_native_get_kms (backend_native);
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
MetaGpuKms *render_gpu = onscreen_native->render_gpu;
MetaDeviceFile *render_device_file;
ClutterFrame *frame = user_data;
CoglOnscreenClass *parent_class;
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;
- MetaKmsCrtc *kms_crtc;
- MetaKmsDevice *kms_device;
- MetaKmsUpdateFlag flags;
- g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
- const GError *feedback_error;
+ size_t rectangles_size;
COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers,
"Onscreen (swap-buffers)");
@@ -1134,6 +1131,39 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
if (egl_context_changed)
_cogl_winsys_egl_ensure_current (cogl_display);
+ rectangles_size = n_rectangles * 4 * sizeof (int);
+ onscreen_native->next_post.rectangles =
+ g_realloc (onscreen_native->next_post.rectangles, rectangles_size);
+ memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size);
+ onscreen_native->next_post.n_rectangles = n_rectangles;
+
+ 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);
+ MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
+ MetaMonitorManager *monitor_manager =
+ meta_backend_get_monitor_manager (backend);
+ MetaKms *kms = meta_backend_native_get_kms (backend_native);
+ 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);
+ MetaKmsUpdateFlag flags;
+ g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
+ const GError *feedback_error;
+
power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
if (power_save_mode == META_POWER_SAVE_ON)
{
@@ -1142,8 +1172,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
onscreen_native->view,
onscreen_native->crtc,
META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE,
- rectangles,
- n_rectangles);
+ onscreen_native->next_post.rectangles,
+ onscreen_native->next_post.n_rectangles);
}
else
{
@@ -2187,6 +2217,8 @@ meta_onscreen_native_dispose (GObject *object)
g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy);
g_clear_pointer (&onscreen_native->secondary_gpu_state,
secondary_gpu_state_free);
+ g_clear_pointer (&onscreen_native->next_post.rectangles, g_free);
+ onscreen_native->next_post.n_rectangles = 0;
}
static void
--
GitLab
From 9621511d643cf361e877406866c9c6e0c227cd51 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Thu, 12 Aug 2021 18:28:57 +0800
Subject: [PATCH 22/44] onscreen/native: Warn instead of crashing if posting
without an update
---
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 1649a9383a..4fa354052c 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1230,6 +1230,7 @@ post_latest_swap (CoglOnscreen *onscreen)
flags = META_KMS_UPDATE_FLAG_NONE;
kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags);
+ g_return_if_fail (kms_feedback != NULL);
switch (meta_kms_feedback_get_result (kms_feedback))
{
--
GitLab
From 6b22f7a3a2f36c3bdc1ccc5996810fcb21f6c75d Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Thu, 28 Oct 2021 17:24:11 +0800
Subject: [PATCH 23/44] onscreen/native: Add a missing frame notification
In the unlikely event that swap_buffers_with_damage drops the previous swap
it was forgetting to notify about the discarded frame. That could lead to
frame clock freezes.
---
src/backends/native/meta-onscreen-native.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 4fa354052c..46e1672b53 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1098,6 +1098,15 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
{
case META_RENDERER_NATIVE_MODE_GBM:
g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
+ if (onscreen_native->gbm.next_fb != NULL)
+ {
+ CoglFrameInfo *frame_info;
+
+ frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
+ frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
+ meta_onscreen_native_notify_frame_complete (onscreen);
+ }
+
if (onscreen_native->secondary_gpu_state)
{
g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb);
--
GitLab
From 86ac0c3a22246cac8be1a53f305d081d4d578555 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 24/44] 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 | 83 ++++++++++++++++++----
1 file changed, 70 insertions(+), 13 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 46e1672b53..dc996001d7 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -94,6 +94,7 @@ struct _MetaOnscreenNative
struct {
struct gbm_surface *surface;
MetaDrmBuffer *next_fb;
+ MetaDrmBuffer *stalled_fb;
} gbm;
#ifdef HAVE_EGL_DEVICE
@@ -106,6 +107,7 @@ struct _MetaOnscreenNative
MetaRendererView *view;
+ unsigned int swaps_pending;
struct {
int *rectangles; /* 4 x n_rectangles */
int n_rectangles;
@@ -116,7 +118,7 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native,
COGL_TYPE_ONSCREEN_EGL)
static void
-post_latest_swap (CoglOnscreen *onscreen);
+try_post_latest_swap (CoglOnscreen *onscreen);
static gboolean
init_secondary_gpu_state (MetaRendererNative *renderer_native,
@@ -187,6 +189,7 @@ notify_view_crtc_presented (MetaRendererView *view,
maybe_update_frame_info (crtc, frame_info, time_us, flags, sequence);
meta_onscreen_native_notify_frame_complete (onscreen);
+ try_post_latest_swap (onscreen);
}
static int64_t
@@ -248,6 +251,7 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc,
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
meta_onscreen_native_notify_frame_complete (onscreen);
+ try_post_latest_swap (onscreen);
}
static void
@@ -297,6 +301,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc,
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
meta_onscreen_native_notify_frame_complete (onscreen);
+ try_post_latest_swap (onscreen);
}
static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = {
@@ -357,19 +362,40 @@ 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);
+ CoglFrameInfo *frame_info;
+
+ /* Remember we can't compare stalled_fb because it's not used by
+ * META_RENDERER_NATIVE_MODE_EGL_DEVICE. So we judge stalled to be whenever
+ * swaps_pending > 1.
+ */
+ if (onscreen_native->swaps_pending <= 1)
+ return;
+
+ onscreen_native->swaps_pending--;
- g_clear_object (&onscreen_native->gbm.next_fb);
+ g_clear_object (&onscreen_native->gbm.stalled_fb);
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
meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
MetaRendererView *view,
@@ -1097,14 +1123,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
switch (renderer_gpu_data->mode)
{
case META_RENDERER_NATIVE_MODE_GBM:
- g_warn_if_fail (onscreen_native->gbm.next_fb == NULL);
if (onscreen_native->gbm.next_fb != NULL)
{
- CoglFrameInfo *frame_info;
-
- frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
- frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
- meta_onscreen_native_notify_frame_complete (onscreen);
+ g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL);
+ drop_stalled_swap (onscreen);
+ g_assert (onscreen_native->gbm.stalled_fb == NULL);
+ onscreen_native->gbm.stalled_fb =
+ g_steal_pointer (&onscreen_native->gbm.next_fb);
}
if (onscreen_native->secondary_gpu_state)
@@ -1146,11 +1171,12 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size);
onscreen_native->next_post.n_rectangles = n_rectangles;
- 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);
@@ -1172,10 +1198,26 @@ post_latest_swap (CoglOnscreen *onscreen)
MetaKmsUpdateFlag flags;
g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
const GError *feedback_error;
+ unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen);
+
+ if (onscreen_native->swaps_pending == 0)
+ return;
+
+ g_assert (frames_pending >= onscreen_native->swaps_pending);
power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
if (power_save_mode == META_POWER_SAVE_ON)
{
+ unsigned int posts_pending;
+
+ posts_pending = frames_pending - onscreen_native->swaps_pending;
+ if (posts_pending > 0)
+ return; /* wait for the next frame notification and then try again */
+
+ drop_stalled_swap (onscreen);
+ g_return_if_fail (onscreen_native->swaps_pending > 0);
+ onscreen_native->swaps_pending--;
+
ensure_crtc_modes (onscreen);
meta_onscreen_native_flip_crtc (onscreen,
onscreen_native->view,
@@ -1332,6 +1374,18 @@ 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_count_pending_frames (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);
@@ -1433,6 +1487,9 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
const GError *error;
+ if (cogl_onscreen_count_pending_frames (onscreen) > 0)
+ return;
+
kms_update = meta_kms_get_pending_update (kms, kms_device);
if (!kms_update)
{
--
GitLab
From 73bbe3d6959e1ec49425127b6ed265665648bde3 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 2 Nov 2021 19:04:04 +0800
Subject: [PATCH 25/44] onscreen/native: Keep a reference to the CRTC used
Otherwise it may get destroyed during hotplug events. Meanwhile we're
still active and might try using it in the next/final frame notification
callbacks.
---
src/backends/native/meta-onscreen-native.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index dc996001d7..86d191ee14 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -2233,7 +2233,7 @@ meta_onscreen_native_new (MetaRendererNative *renderer_native,
onscreen_native->renderer_native = renderer_native;
onscreen_native->render_gpu = render_gpu;
onscreen_native->output = output;
- onscreen_native->crtc = crtc;
+ onscreen_native->crtc = g_object_ref (crtc);
return onscreen_native;
}
@@ -2281,6 +2281,7 @@ meta_onscreen_native_dispose (GObject *object)
G_OBJECT_CLASS (meta_onscreen_native_parent_class)->dispose (object);
+ g_clear_object (&onscreen_native->crtc);
g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy);
g_clear_pointer (&onscreen_native->secondary_gpu_state,
secondary_gpu_state_free);
--
GitLab
From 1644e8ac2254e46f8e7de3495f9f2bb5662e4447 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 26/44] 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 86d191ee14..fb42ba7f4b 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1529,6 +1529,17 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
}
}
+void
+meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen)
+{
+ MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
+
+ onscreen_native->swaps_pending = 0;
+
+ g_clear_object (&onscreen_native->gbm.stalled_fb);
+ g_clear_object (&onscreen_native->gbm.next_fb);
+}
+
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 3a85ace267..676c4c4459 100644
--- a/src/backends/native/meta-onscreen-native.h
+++ b/src/backends/native/meta-onscreen-native.h
@@ -40,6 +40,8 @@ void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen);
+void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen);
+
gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen,
MetaDrmBuffer *fb);
--
GitLab
From eeb77174132bccdf585ffb01a8681a0e86579d2c 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 27/44] 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 a4ef05682d..58e6339a8c 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -1395,6 +1395,26 @@ meta_renderer_native_create_view (MetaRenderer *renderer,
return view;
}
+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
keep_current_onscreens_alive (MetaRenderer *renderer)
{
@@ -1423,6 +1443,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);
meta_kms_discard_pending_updates (kms);
--
GitLab
From 5d93028632ebea30d2862f9415dfab97d58910a7 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 28/44] 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 6fa2b25887..bf6cf3ef4c 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -670,7 +670,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 e9f53325961918e8bc4522494ed99a98a1c9e5eb 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 29/44] 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 | 26 ++++++++++----------------
1 file changed, 10 insertions(+), 16 deletions(-)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index bf6cf3ef4c..26f2f681e5 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -65,8 +65,7 @@ typedef enum _ClutterFrameClockState
CLUTTER_FRAME_CLOCK_STATE_INIT,
CLUTTER_FRAME_CLOCK_STATE_IDLE,
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED,
- CLUTTER_FRAME_CLOCK_STATE_DISPATCHING,
- CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED,
} ClutterFrameClockState;
struct _ClutterFrameClock
@@ -317,8 +316,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
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;
@@ -337,8 +335,7 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
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;
@@ -558,8 +555,7 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock)
frame_clock->pending_reschedule = 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;
}
@@ -598,8 +594,7 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
break;
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
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;
@@ -637,8 +632,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
break;
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
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;
}
@@ -678,7 +672,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++;
@@ -706,19 +700,19 @@ 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:
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;
--
GitLab
From 856b06a53a6c74b5b7d7e2f52ae8c8f0072b03a5 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 30/44] clutter/frame-clock: Add triple buffering support
---
clutter/clutter/clutter-frame-clock.c | 101 ++++++++++++++++++++------
1 file changed, 80 insertions(+), 21 deletions(-)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index 26f2f681e5..cacd4f6362 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -65,7 +65,9 @@ typedef enum _ClutterFrameClockState
CLUTTER_FRAME_CLOCK_STATE_INIT,
CLUTTER_FRAME_CLOCK_STATE_IDLE,
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED,
- CLUTTER_FRAME_CLOCK_STATE_DISPATCHED,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED,
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO,
} ClutterFrameClockState;
struct _ClutterFrameClock
@@ -316,10 +318,18 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
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_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ maybe_reschedule_update (frame_clock);
+ break;
}
}
@@ -335,10 +345,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
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_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ maybe_reschedule_update (frame_clock);
+ break;
}
}
@@ -555,7 +573,12 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock)
frame_clock->pending_reschedule = 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:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
break;
}
@@ -591,10 +614,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_INIT:
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
next_update_time_us = g_get_monotonic_time ();
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
break;
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
return;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ next_update_time_us = g_get_monotonic_time ();
+ frame_clock->state =
+ CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
frame_clock->pending_reschedule = TRUE;
frame_clock->pending_reschedule_now = TRUE;
return;
@@ -603,7 +633,6 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
g_warn_if_fail (next_update_time_us != -1);
g_source_set_ready_time (frame_clock->source, next_update_time_us);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
frame_clock->is_next_presentation_time_valid = FALSE;
}
@@ -622,6 +651,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
{
case CLUTTER_FRAME_CLOCK_STATE_INIT:
next_update_time_us = g_get_monotonic_time ();
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
break;
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
calculate_next_update_time_us (frame_clock,
@@ -629,10 +659,20 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
&frame_clock->next_presentation_time_us);
frame_clock->is_next_presentation_time_valid =
(frame_clock->next_presentation_time_us != 0);
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
break;
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
return;
- case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ calculate_next_update_time_us (frame_clock,
+ &next_update_time_us,
+ &frame_clock->next_presentation_time_us);
+ frame_clock->is_next_presentation_time_valid =
+ (frame_clock->next_presentation_time_us != 0);
+ 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;
}
@@ -640,7 +680,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
g_warn_if_fail (next_update_time_us != -1);
g_source_set_ready_time (frame_clock->source, next_update_time_us);
- frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
}
static void
@@ -672,7 +711,21 @@ 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_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:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO;
+ break;
+ }
frame_count = frame_clock->frame_count++;
@@ -697,25 +750,31 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
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:
- 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:
+ 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_TWO:
+ frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE;
+ maybe_reschedule_update (frame_clock);
+ break;
}
break;
}
--
GitLab
From 171608c43d38761abc8933331a3750b72f7bf1f9 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 31/44] clutter/frame-clock: Log N-buffers in
CLUTTTER_DEBUG=frame-timings
---
clutter/clutter/clutter-frame-clock.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index cacd4f6362..e97c6f2ad4 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -229,6 +229,10 @@ void
clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
ClutterFrameInfo *frame_info)
{
+ const char *debug_state =
+ frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ?
+ "Triple buffering" : "Double buffering";
+
COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented,
"Frame Clock (presented)");
@@ -290,7 +294,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
frame_info->cpu_time_before_buffer_swap_us;
CLUTTER_NOTE (FRAME_TIMINGS,
- "dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs",
+ "%s: dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs",
+ debug_state,
dispatch_to_swap_us,
swap_to_rendering_done_us,
swap_to_flip_us);
@@ -304,6 +309,10 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
frame_clock->got_measurements_last_frame = TRUE;
}
+ else
+ {
+ CLUTTER_NOTE (FRAME_TIMINGS, "%s", debug_state);
+ }
if (frame_info->refresh_rate > 1)
{
--
GitLab
From 8dd2b170d40cf556ee4d7cdc5ff05b0ab653b85c 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 32/44] clutter/frame-clock: Don't clamp
compute_max_render_time_us
Because values over refresh_interval_us are real and valid. They
tell us when we're not keeping up.
---
clutter/clutter/clutter-frame-clock.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index e97c6f2ad4..ade6f2e4a5 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -415,8 +415,6 @@ 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);
-
return max_render_time_us;
}
--
GitLab
From b9c6aaa146770ed47454db487dcac2e41323e17b Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Thu, 17 Feb 2022 15:54:57 +0800
Subject: [PATCH 33/44] clutter/frame-clock: Subtract buffer queue latency from
max_swap_to_flip_us
swap_to_flip times were getting stuck at over one frame interval
because of a positive feedback loop with triple buffering.
The moment triple buffering started it would cause swap_to_flip times
to exceed one frame interval. Then that high value would feed back
through max_render_time which in turn ensured triple buffering
continued which ensured swap_to_flip stayed over one frame interval.
So even when actual GPU render times became low we were stuck with
a high value of max_render_time and stuck in triple buffering when we
should have switched to double buffering.
By subtracting the constant buffer queue latency from max_swap_to_flip_us
we break this positive feedback loop and only include real performance
measurements. So now the frame clock is able to drop back to double
buffering when appropriate.
---
clutter/clutter/clutter-frame-clock.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index ade6f2e4a5..cc4ac21c24 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -377,6 +377,7 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
int64_t max_swap_to_rendering_done_us = 0;
int64_t max_swap_to_flip_us = 0;
int64_t max_render_time_us;
+ int buffer_queue_latency_frames = 0;
int i;
refresh_interval_us = frame_clock->refresh_interval_us;
@@ -399,6 +400,27 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
frame_clock->swap_to_flip_us.values[i]);
}
+ switch (frame_clock->state)
+ {
+ case CLUTTER_FRAME_CLOCK_STATE_INIT:
+ case CLUTTER_FRAME_CLOCK_STATE_IDLE:
+ case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
+ buffer_queue_latency_frames = 0;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
+ buffer_queue_latency_frames = 1;
+ break;
+ case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
+ g_warn_if_reached ();
+ buffer_queue_latency_frames = 2;
+ break;
+ }
+
+ max_swap_to_flip_us -= refresh_interval_us * buffer_queue_latency_frames;
+ if (max_swap_to_flip_us < 0)
+ max_swap_to_flip_us = 0;
+
/* Max render time shows how early the frame clock needs to be dispatched
* to make it to the predicted next presentation time. It is composed of:
* - An estimate of duration from dispatch start to buffer swap.
--
GitLab
From 4855f39c21b7e98c42df4c8e2573019c2573dc5a 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 34/44] 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 91e6b3a130..18c7676f4e 100644
--- a/clutter/clutter/clutter-frame-clock.h
+++ b/clutter/clutter/clutter-frame-clock.h
@@ -34,6 +34,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 e0088564fc..06581492f5 100644
--- a/clutter/clutter/clutter-frame-private.h
+++ b/clutter/clutter/clutter-frame-private.h
@@ -24,6 +24,7 @@ struct _ClutterFrame
{
gboolean has_result;
ClutterFrameResult result;
+ ClutterFrameHint hints;
};
#define CLUTTER_FRAME_INIT ((ClutterFrame) { 0 })
diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c
index 3c708da9dc..63ae302af7 100644
--- a/clutter/clutter/clutter-frame.c
+++ b/clutter/clutter/clutter-frame.c
@@ -40,3 +40,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 d3608e81ca..06c5f7f28a 100644
--- a/clutter/clutter/clutter-frame.h
+++ b/clutter/clutter/clutter-frame.h
@@ -33,4 +33,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);
+
#endif /* CLUTTER_FRAME_H */
--
GitLab
From 3bdb988fd0940922e53519f13486ae784f5131db 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 35/44] 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 c45aaf852e..683f4ff6c5 100644
--- a/src/backends/meta-stage-impl.c
+++ b/src/backends/meta-stage-impl.c
@@ -720,6 +720,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 97e6394f725c432ade1e824746a84f690227dbff 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 36/44] 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 | 5 +++--
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index cc4ac21c24..577f10ee71 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -97,6 +97,8 @@ struct _ClutterFrameClock
/* Last KMS buffer submission time. */
int64_t last_flip_time_us;
+ ClutterFrameHint last_flip_hints;
+
/* Last few durations between dispatch start and buffer swap. */
EstimateQueue dispatch_to_swap_us;
/* Last few durations between buffer swap and GPU rendering finish. */
@@ -836,10 +838,12 @@ 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->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 18c7676f4e..d750404d53 100644
--- a/clutter/clutter/clutter-frame-clock.h
+++ b/clutter/clutter/clutter-frame-clock.h
@@ -96,8 +96,9 @@ 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 8a82de71ed..45d0093521 100644
--- a/clutter/clutter/clutter-stage-view.c
+++ b/clutter/clutter/clutter-stage-view.c
@@ -1190,8 +1190,9 @@ 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);
--
GitLab
From 9649fbb9cf95d8e0d044b48811ed753ccc25ae32 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 37/44] clutter/frame-clock: Throttle back to double buffering
for direct scanout
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.
---
clutter/clutter/clutter-frame-clock.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index 577f10ee71..57176b3763 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -696,6 +696,13 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
return;
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
+ if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED)
+ {
+ /* Force double buffering, disable triple buffering */
+ frame_clock->pending_reschedule = TRUE;
+ return;
+ }
+
calculate_next_update_time_us (frame_clock,
&next_update_time_us,
&frame_clock->next_presentation_time_us);
--
GitLab
From e1d01f6cb3963a39e8c07e10f4e12e77b9762d18 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Thu, 10 Mar 2022 16:44:14 +0800
Subject: [PATCH 38/44] clutter/frame-clock: Add env var
MUTTER_DEBUG_DISABLE_TRIPLE_BUFFERING
You can set environment variable MUTTER_DEBUG_DISABLE_TRIPLE_BUFFERING=1
to disable triple buffering. This shouldn't ever be needed except when
debugging.
---
clutter/clutter/clutter-frame-clock.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index 57176b3763..7a6444ec44 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -45,6 +45,8 @@ typedef struct _EstimateQueue
int next_index;
} EstimateQueue;
+static gboolean triple_buffering_disabled = FALSE;
+
#define SYNC_DELAY_FALLBACK_FRACTION 0.875
typedef struct _ClutterFrameListener
@@ -696,7 +698,8 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
return;
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
- if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED)
+ if (frame_clock->last_flip_hints & CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED ||
+ triple_buffering_disabled)
{
/* Force double buffering, disable triple buffering */
frame_clock->pending_reschedule = TRUE;
@@ -981,6 +984,9 @@ clutter_frame_clock_class_init (ClutterFrameClockClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ if (!g_strcmp0 (g_getenv ("MUTTER_DEBUG_DISABLE_TRIPLE_BUFFERING"), "1"))
+ triple_buffering_disabled = TRUE;
+
object_class->dispose = clutter_frame_clock_dispose;
signals[DESTROY] =
--
GitLab
From 1fe109cf7a96f8521bcef30910ac41007cd5d467 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 22 Sep 2021 17:01:42 +0800
Subject: [PATCH 39/44] kms: Keep a hash table of CRTCs relating to the
KmsUpdate
This will allow more efficient lookup than linear searching each of
the GLists.
---
src/backends/native/meta-kms-update.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c
index 2889338ec5..5896d66dfe 100644
--- a/src/backends/native/meta-kms-update.c
+++ b/src/backends/native/meta-kms-update.c
@@ -31,6 +31,7 @@
struct _MetaKmsUpdate
{
MetaKmsDevice *device;
+ GHashTable *crtcs;
gboolean is_locked;
uint64_t sequence_number;
@@ -238,6 +239,8 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update,
update->plane_assignments = g_list_prepend (update->plane_assignments,
plane_assignment);
+ g_hash_table_insert (update->crtcs, crtc, NULL);
+
return plane_assignment;
}
@@ -263,6 +266,8 @@ meta_kms_update_unassign_plane (MetaKmsUpdate *update,
update->plane_assignments = g_list_prepend (update->plane_assignments,
plane_assignment);
+ g_hash_table_insert (update->crtcs, crtc, NULL);
+
return plane_assignment;
}
@@ -285,6 +290,8 @@ meta_kms_update_mode_set (MetaKmsUpdate *update,
};
update->mode_sets = g_list_prepend (update->mode_sets, mode_set);
+
+ g_hash_table_insert (update->crtcs, crtc, NULL);
}
static MetaKmsConnectorUpdate *
@@ -403,6 +410,8 @@ meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update,
gamma = meta_kms_crtc_gamma_new (crtc, size, red, green, blue);
update->crtc_gammas = g_list_prepend (update->crtc_gammas, gamma);
+
+ g_hash_table_insert (update->crtcs, crtc, NULL);
}
void
@@ -694,12 +703,15 @@ meta_kms_update_new (MetaKmsDevice *device)
update->device = device;
update->sequence_number = sequence_number++;
+ update->crtcs = g_hash_table_new (NULL, NULL);
+
return update;
}
void
meta_kms_update_free (MetaKmsUpdate *update)
{
+ g_hash_table_destroy (update->crtcs);
g_list_free_full (update->result_listeners,
(GDestroyNotify) meta_kms_result_listener_free);
g_list_free_full (update->plane_assignments,
--
GitLab
From 043be2e16d4c8afebb749181962d884d97451292 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 22 Sep 2021 17:19:45 +0800
Subject: [PATCH 40/44] kms: Add functions meta_kms_update_include[s]_crtc()
---
src/backends/native/meta-kms-update-private.h | 6 ++++++
src/backends/native/meta-kms-update.c | 14 ++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h
index a613cbc5d6..1d964ff21f 100644
--- a/src/backends/native/meta-kms-update-private.h
+++ b/src/backends/native/meta-kms-update-private.h
@@ -132,6 +132,12 @@ uint64_t meta_kms_update_get_sequence_number (MetaKmsUpdate *update);
META_EXPORT_TEST
MetaKmsDevice * meta_kms_update_get_device (MetaKmsUpdate *update);
+gboolean meta_kms_update_includes_crtc (MetaKmsUpdate *update,
+ MetaKmsCrtc *crtc);
+
+void meta_kms_update_include_crtc (MetaKmsUpdate *update,
+ MetaKmsCrtc *crtc);
+
void meta_kms_plane_assignment_set_rotation (MetaKmsPlaneAssignment *plane_assignment,
uint64_t rotation);
diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c
index 5896d66dfe..1e43f3f0f5 100644
--- a/src/backends/native/meta-kms-update.c
+++ b/src/backends/native/meta-kms-update.c
@@ -675,6 +675,20 @@ meta_kms_update_get_device (MetaKmsUpdate *update)
return update->device;
}
+gboolean
+meta_kms_update_includes_crtc (MetaKmsUpdate *update,
+ MetaKmsCrtc *crtc)
+{
+ return g_hash_table_lookup_extended (update->crtcs, crtc, NULL, NULL);
+}
+
+void
+meta_kms_update_include_crtc (MetaKmsUpdate *update,
+ MetaKmsCrtc *crtc)
+{
+ g_hash_table_insert (update->crtcs, crtc, NULL);
+}
+
MetaKmsCustomPageFlip *
meta_kms_update_take_custom_page_flip_func (MetaKmsUpdate *update)
{
--
GitLab
From db5653698234487b006321b4198f4c225394ede0 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Wed, 22 Sep 2021 18:13:54 +0800
Subject: [PATCH 41/44] kms: Add per-CRTC update functions
---
src/backends/native/meta-kms.c | 103 +++++++++++++++++++++++++++++++--
src/backends/native/meta-kms.h | 10 ++++
2 files changed, 109 insertions(+), 4 deletions(-)
diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c
index 052ec8a65c..9acc17b0d0 100644
--- a/src/backends/native/meta-kms.c
+++ b/src/backends/native/meta-kms.c
@@ -23,6 +23,7 @@
#include "backends/native/meta-kms-private.h"
#include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-kms-crtc.h"
#include "backends/native/meta-kms-device-private.h"
#include "backends/native/meta-kms-impl.h"
#include "backends/native/meta-kms-update-private.h"
@@ -181,6 +182,11 @@ struct _MetaKms
G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT)
+static MetaKmsFeedback *
+meta_kms_post_update_sync (MetaKms *kms,
+ MetaKmsUpdate *update,
+ MetaKmsUpdateFlag flags);
+
void
meta_kms_discard_pending_updates (MetaKms *kms)
{
@@ -247,12 +253,105 @@ meta_kms_take_pending_update (MetaKms *kms,
return NULL;
}
+MetaKmsUpdate *
+meta_kms_ensure_pending_update_for_crtc (MetaKms *kms,
+ MetaKmsCrtc *crtc)
+{
+ MetaKmsUpdate *update;
+
+ update = meta_kms_get_pending_update_for_crtc (kms, crtc);
+ if (update == NULL)
+ {
+ update = meta_kms_update_new (meta_kms_crtc_get_device (crtc));
+ meta_kms_update_include_crtc (update, crtc);
+ meta_kms_add_pending_update (kms, update);
+ }
+
+ return update;
+}
+
+static MetaKmsUpdate *
+meta_kms_find_compatible_update_for_crtc (MetaKms *kms,
+ MetaKmsCrtc *crtc,
+ gboolean take)
+{
+ MetaKmsDevice *device;
+ MetaKmsUpdate *update;
+ GList *l;
+
+ for (l = kms->pending_updates; l; l = l->next)
+ {
+ update = l->data;
+ if (meta_kms_update_includes_crtc (update, crtc))
+ goto found;
+ }
+
+ device = meta_kms_crtc_get_device (crtc);
+
+ for (l = kms->pending_updates; l; l = l->next)
+ {
+ update = l->data;
+ if (meta_kms_update_get_device (update) == device &&
+ meta_kms_update_get_mode_sets (update))
+ goto found;
+ }
+
+ return NULL;
+
+found:
+ if (take)
+ kms->pending_updates = g_list_delete_link (kms->pending_updates, l);
+ return update;
+}
+
+MetaKmsUpdate *
+meta_kms_get_pending_update_for_crtc (MetaKms *kms,
+ MetaKmsCrtc *crtc)
+{
+ return meta_kms_find_compatible_update_for_crtc (kms, crtc, FALSE);
+}
+
+static MetaKmsUpdate *
+meta_kms_take_pending_update_for_crtc (MetaKms *kms,
+ MetaKmsCrtc *crtc)
+{
+ return meta_kms_find_compatible_update_for_crtc (kms, crtc, TRUE);
+}
+
MetaKmsFeedback *
meta_kms_post_pending_update_sync (MetaKms *kms,
MetaKmsDevice *device,
MetaKmsUpdateFlag flags)
{
MetaKmsUpdate *update;
+
+ update = meta_kms_take_pending_update (kms, device);
+ if (!update)
+ return NULL;
+
+ return meta_kms_post_update_sync (kms, update, flags);
+}
+
+MetaKmsFeedback *
+meta_kms_post_pending_update_for_crtc_sync (MetaKms *kms,
+ MetaKmsCrtc *crtc,
+ MetaKmsUpdateFlag flags)
+{
+ MetaKmsUpdate *update;
+
+ update = meta_kms_take_pending_update_for_crtc (kms, crtc);
+ if (!update)
+ return NULL;
+
+ return meta_kms_post_update_sync (kms, update, flags);
+}
+
+static MetaKmsFeedback *
+meta_kms_post_update_sync (MetaKms *kms,
+ MetaKmsUpdate *update,
+ MetaKmsUpdateFlag flags)
+{
+ MetaKmsDevice *device = meta_kms_update_get_device (update);
MetaKmsFeedback *feedback;
GList *result_listeners;
GList *l;
@@ -260,10 +359,6 @@ meta_kms_post_pending_update_sync (MetaKms *kms,
COGL_TRACE_BEGIN_SCOPED (MetaKmsPostUpdateSync,
"KMS (post update)");
- update = meta_kms_take_pending_update (kms, device);
- if (!update)
- return NULL;
-
meta_kms_update_lock (update);
feedback = meta_kms_device_process_update_sync (device, update, flags);
diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h
index bd9fe5cead..84f1bed498 100644
--- a/src/backends/native/meta-kms.h
+++ b/src/backends/native/meta-kms.h
@@ -39,9 +39,15 @@ void meta_kms_discard_pending_updates (MetaKms *kms);
MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms,
MetaKmsDevice *device);
+MetaKmsUpdate * meta_kms_ensure_pending_update_for_crtc (MetaKms *kms,
+ MetaKmsCrtc *crtc);
+
MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms,
MetaKmsDevice *device);
+MetaKmsUpdate * meta_kms_get_pending_update_for_crtc (MetaKms *kms,
+ MetaKmsCrtc *crtc);
+
MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms,
MetaKmsDevice *device,
MetaKmsUpdateFlag flags);
@@ -49,6 +55,10 @@ MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms,
MetaKmsFeedback * meta_kms_post_test_update_sync (MetaKms *kms,
MetaKmsUpdate *update);
+MetaKmsFeedback * meta_kms_post_pending_update_for_crtc_sync (MetaKms *kms,
+ MetaKmsCrtc *device,
+ MetaKmsUpdateFlag flags);
+
void meta_kms_discard_pending_page_flips (MetaKms *kms);
void meta_kms_notify_modes_set (MetaKms *kms);
--
GitLab
From c48a8338a6a6ec7f20d9b072faf8f6d84509ea48 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Fri, 28 Jan 2022 15:46:09 +0800
Subject: [PATCH 42/44] cursor-renderer/native: Use CRTC-specific update
retrieval
---
src/backends/native/meta-cursor-renderer-native.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
index 998af173d3..606fcad97f 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -291,8 +291,8 @@ assign_cursor_plane (MetaCursorRendererNative *native,
flags |= META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED;
kms_update =
- meta_kms_ensure_pending_update (meta_kms_device_get_kms (kms_device),
- meta_kms_crtc_get_device (kms_crtc));
+ meta_kms_ensure_pending_update_for_crtc (meta_kms_device_get_kms (kms_device),
+ kms_crtc);
plane_assignment = meta_kms_update_assign_plane (kms_update,
kms_crtc,
cursor_plane,
@@ -440,7 +440,7 @@ unset_crtc_cursor (MetaCursorRendererNative *native,
MetaKms *kms = meta_kms_device_get_kms (kms_device);
MetaKmsUpdate *kms_update;
- kms_update = meta_kms_ensure_pending_update (kms, kms_device);
+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc);
meta_kms_update_unassign_plane (kms_update, kms_crtc, cursor_plane);
}
--
GitLab
From fe9b03f65bc168fd10aecdeae865405aa9e93aef Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Fri, 28 Jan 2022 15:50:24 +0800
Subject: [PATCH 43/44] crtc/native: Use CRTC-specific update retrieval
---
src/backends/native/meta-crtc-kms.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c
index 584a780ba8..773b540b47 100644
--- a/src/backends/native/meta-crtc-kms.c
+++ b/src/backends/native/meta-crtc-kms.c
@@ -211,6 +211,7 @@ meta_crtc_kms_maybe_set_gamma (MetaCrtcKms *crtc_kms,
MetaMonitorManagerNative *monitor_manager_native =
META_MONITOR_MANAGER_NATIVE (monitor_manager);
MetaKms *kms = meta_kms_device_get_kms (kms_device);
+ MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
MetaKmsUpdate *kms_update;
MetaKmsCrtcGamma *gamma;
@@ -222,9 +223,9 @@ meta_crtc_kms_maybe_set_gamma (MetaCrtcKms *crtc_kms,
if (!gamma)
return;
- kms_update = meta_kms_ensure_pending_update (kms, kms_device);
+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc);
meta_kms_update_set_crtc_gamma (kms_update,
- meta_crtc_kms_get_kms_crtc (crtc_kms),
+ kms_crtc,
gamma->size,
gamma->red,
gamma->green,
--
GitLab
From 163776ae49fa9af98405d8225be6e23473d550dc Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Fri, 28 Jan 2022 16:35:42 +0800
Subject: [PATCH 44/44] onscreen/native: Use CRTC-specific update retrieval
For mode setting we want all CRTCs to share the same MetaKmsUpdate, but
after that we don't want them to share the same update for regular flips
and cursor movement. The reason being that different CRTCs are attached to
different displays with different clocks. So each CRTC would finish the
same update at slightly different times.
If you wait for all CRTCs to complete a shared update then you have waited
half a frame on average, possibly more. This waiting would consume most of
the render time for the next frame and risk stuttering.
If you only wait for one CRTC to complete a shared update then the next
shared update may be posted before other CRTCs have finished the last, so
the next update would fail due to those other CRTCs still being busy.
So the only safe answer for maintaining full framerate of multiple outputs
is to ensure unsynchronized CRTCs don't share the same update. At least
not after mode setting.
This fixes a multi-monitor bug for atomic KMS whereby
`meta_onscreen_native_finish_frame` would fail its post because it was
trying to commit a cursor update on the wrong (busy) CRTC.
---
src/backends/native/meta-onscreen-native.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index fb42ba7f4b..24d7f73852 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -423,7 +423,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
kms = meta_kms_device_get_kms (kms_device);
- kms_update = meta_kms_ensure_pending_update (kms, kms_device);
+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc);
g_assert (meta_gpu_kms_is_crtc_active (gpu_kms, crtc));
@@ -478,7 +478,7 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen,
COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeSetCrtcModes,
"Onscreen (set CRTC modes)");
- kms_update = meta_kms_ensure_pending_update (kms, kms_device);
+ kms_update = meta_kms_ensure_pending_update_for_crtc (kms, kms_crtc);
switch (renderer_gpu_data->mode)
{
@@ -1280,7 +1280,9 @@ try_post_latest_swap (CoglOnscreen *onscreen)
meta_kms_device_get_path (kms_device));
flags = META_KMS_UPDATE_FLAG_NONE;
- kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags);
+ kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms,
+ kms_crtc,
+ flags);
g_return_if_fail (kms_feedback != NULL);
switch (meta_kms_feedback_get_result (kms_feedback))
@@ -1438,7 +1440,9 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
meta_kms_device_get_path (kms_device));
flags = META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR;
- kms_feedback = meta_kms_post_pending_update_sync (kms, kms_device, flags);
+ kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms,
+ kms_crtc,
+ flags);
switch (meta_kms_feedback_get_result (kms_feedback))
{
case META_KMS_FEEDBACK_PASSED:
@@ -1490,7 +1494,7 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
if (cogl_onscreen_count_pending_frames (onscreen) > 0)
return;
- kms_update = meta_kms_get_pending_update (kms, kms_device);
+ kms_update = meta_kms_get_pending_update_for_crtc (kms, kms_crtc);
if (!kms_update)
{
clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE);
@@ -1505,9 +1509,9 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
g_object_unref);
flags = META_KMS_UPDATE_FLAG_NONE;
- kms_feedback = meta_kms_post_pending_update_sync (kms,
- kms_device,
- flags);
+ kms_feedback = meta_kms_post_pending_update_for_crtc_sync (kms,
+ kms_crtc,
+ flags);
switch (meta_kms_feedback_get_result (kms_feedback))
{
case META_KMS_FEEDBACK_PASSED:
--
GitLab