From 6e3ecadb79b46e366777541834899105808e8215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 10 Sep 2020 17:41:06 +0200 Subject: [PATCH] backends/native: Fall back to compositing if direct scanout failed Even when a direct client buffer has a compatible format, stride and modifier for direct scanout, drmModePageFlip() may still fail sometimes. From testing, it has been observed that it may seemingly randomly fail with ENOSPC, where all subsequent attempts later on the same CRTC failing with EBUSY. Handle this by falling back to flipping after having composited a full frame again. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1410 --- clutter/clutter/cogl/clutter-stage-cogl.c | 33 ++++++++---- cogl/cogl/cogl-onscreen.c | 20 ++++--- cogl/cogl/cogl-onscreen.h | 9 ++-- cogl/cogl/winsys/cogl-winsys-private.h | 9 ++-- src/backends/native/meta-crtc-kms.c | 2 + src/backends/native/meta-crtc-kms.h | 2 + src/backends/native/meta-kms-impl-simple.c | 3 ++ src/backends/native/meta-kms-update-private.h | 1 + src/backends/native/meta-kms-update.c | 2 + src/backends/native/meta-kms-update.h | 7 +++ src/backends/native/meta-renderer-native.c | 54 +++++++++++++------ 11 files changed, 103 insertions(+), 39 deletions(-) diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c index 9baccbd0e..b68266200 100644 --- a/clutter/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/clutter/cogl/clutter-stage-cogl.c @@ -649,10 +649,11 @@ clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl, cairo_region_destroy (swap_region); } -static void -clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl, - ClutterStageView *view, - CoglScanout *scanout) +static gboolean +clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl, + ClutterStageView *view, + CoglScanout *scanout, + GError **error) { ClutterStageCoglPrivate *priv = _clutter_stage_cogl_get_instance_private (stage_cogl); @@ -660,14 +661,21 @@ clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl, CoglOnscreen *onscreen; CoglFrameInfo *frame_info; - g_return_if_fail (cogl_is_onscreen (framebuffer)); + g_assert (cogl_is_onscreen (framebuffer)); onscreen = COGL_ONSCREEN (framebuffer); frame_info = cogl_frame_info_new (priv->global_frame_counter); + + if (!cogl_onscreen_direct_scanout (onscreen, scanout, frame_info, error)) + { + cogl_object_unref (frame_info); + return FALSE; + } + priv->global_frame_counter++; - cogl_onscreen_direct_scanout (onscreen, scanout, frame_info); + return TRUE; } static void @@ -679,9 +687,16 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, scanout = clutter_stage_view_take_scanout (view); if (scanout) - clutter_stage_cogl_scanout_view (stage_cogl, view, scanout); - else - clutter_stage_cogl_redraw_view_primary (stage_cogl, view); + { + g_autoptr (GError) error = NULL; + + if (clutter_stage_cogl_scanout_view (stage_cogl, view, scanout, &error)) + return; + + g_warning ("Failed to scan out client buffer: %s", error->message); + } + + clutter_stage_cogl_redraw_view_primary (stage_cogl, view); } static void diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c index a3c1514a5..4f3f48fbe 100644 --- a/cogl/cogl/cogl-onscreen.c +++ b/cogl/cogl/cogl-onscreen.c @@ -406,24 +406,30 @@ cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen) return winsys->onscreen_get_buffer_age (onscreen); } -void -cogl_onscreen_direct_scanout (CoglOnscreen *onscreen, - CoglScanout *scanout, - CoglFrameInfo *info) +gboolean +cogl_onscreen_direct_scanout (CoglOnscreen *onscreen, + CoglScanout *scanout, + CoglFrameInfo *info, + GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys; - g_return_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); - g_return_if_fail (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)); + g_warn_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); + g_warn_if_fail (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)); info->frame_counter = onscreen->frame_counter; g_queue_push_tail (&onscreen->pending_frame_infos, info); winsys = _cogl_framebuffer_get_winsys (framebuffer); - winsys->onscreen_direct_scanout (onscreen, scanout, info); + if (!winsys->onscreen_direct_scanout (onscreen, scanout, info, error)) + { + g_queue_pop_tail (&onscreen->pending_frame_infos); + return FALSE; + } onscreen->frame_counter++; + return TRUE; } #ifdef COGL_HAS_X11_SUPPORT diff --git a/cogl/cogl/cogl-onscreen.h b/cogl/cogl/cogl-onscreen.h index b0cd99fce..824672f5e 100644 --- a/cogl/cogl/cogl-onscreen.h +++ b/cogl/cogl/cogl-onscreen.h @@ -291,10 +291,11 @@ cogl_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, /** * cogl_onscreen_direct_scanout: (skip) */ -COGL_EXPORT void -cogl_onscreen_direct_scanout (CoglOnscreen *onscreen, - CoglScanout *scanout, - CoglFrameInfo *info); +COGL_EXPORT gboolean +cogl_onscreen_direct_scanout (CoglOnscreen *onscreen, + CoglScanout *scanout, + CoglFrameInfo *info, + GError **error); /** * cogl_onscreen_swap_region: diff --git a/cogl/cogl/winsys/cogl-winsys-private.h b/cogl/cogl/winsys/cogl-winsys-private.h index b9dbff524..2cf88f39b 100644 --- a/cogl/cogl/winsys/cogl-winsys-private.h +++ b/cogl/cogl/winsys/cogl-winsys-private.h @@ -119,10 +119,11 @@ typedef struct _CoglWinsysVtable int n_rectangles, CoglFrameInfo *info); - void - (*onscreen_direct_scanout) (CoglOnscreen *onscreen, - CoglScanout *scanout, - CoglFrameInfo *info); + gboolean + (*onscreen_direct_scanout) (CoglOnscreen *onscreen, + CoglScanout *scanout, + CoglFrameInfo *info, + GError **error); void (*onscreen_set_visibility) (CoglOnscreen *onscreen, diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c index 9501fc839..385e828cc 100644 --- a/src/backends/native/meta-crtc-kms.c +++ b/src/backends/native/meta-crtc-kms.c @@ -206,12 +206,14 @@ meta_crtc_kms_set_mode (MetaCrtcKms *crtc_kms, void meta_crtc_kms_page_flip (MetaCrtcKms *crtc_kms, const MetaKmsPageFlipFeedback *page_flip_feedback, + MetaKmsPageFlipFlag flags, gpointer user_data, MetaKmsUpdate *kms_update) { meta_kms_update_page_flip (kms_update, meta_crtc_kms_get_kms_crtc (crtc_kms), page_flip_feedback, + flags, user_data); } diff --git a/src/backends/native/meta-crtc-kms.h b/src/backends/native/meta-crtc-kms.h index 9ca80c438..3e3e1a6e8 100644 --- a/src/backends/native/meta-crtc-kms.h +++ b/src/backends/native/meta-crtc-kms.h @@ -30,6 +30,7 @@ #include "backends/meta-crtc.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-crtc.h" +#include "backends/native/meta-kms-update.h" #define META_TYPE_CRTC_KMS (meta_crtc_kms_get_type ()) G_DECLARE_FINAL_TYPE (MetaCrtcKms, meta_crtc_kms, @@ -56,6 +57,7 @@ void meta_crtc_kms_set_mode (MetaCrtcKms *crtc_kms, void meta_crtc_kms_page_flip (MetaCrtcKms *crtc_kms, const MetaKmsPageFlipFeedback *page_flip_feedback, + MetaKmsPageFlipFlag flags, gpointer user_data, MetaKmsUpdate *kms_update); diff --git a/src/backends/native/meta-kms-impl-simple.c b/src/backends/native/meta-kms-impl-simple.c index 5aa7adc08..1afc89f74 100644 --- a/src/backends/native/meta-kms-impl-simple.c +++ b/src/backends/native/meta-kms-impl-simple.c @@ -988,6 +988,9 @@ err_planes_assigned: { MetaKmsPageFlip *page_flip = l->data; + if (page_flip->flags & META_KMS_PAGE_FLIP_FLAG_NO_DISCARD_FEEDBACK) + continue; + discard_page_flip (impl, update, page_flip); } diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h index 4d4d4a276..df886b8dd 100644 --- a/src/backends/native/meta-kms-update-private.h +++ b/src/backends/native/meta-kms-update-private.h @@ -87,6 +87,7 @@ typedef struct _MetaKmsPageFlip { MetaKmsCrtc *crtc; const MetaKmsPageFlipFeedback *feedback; + MetaKmsPageFlipFlag flags; gpointer user_data; MetaKmsCustomPageFlipFunc custom_page_flip_func; gpointer custom_page_flip_user_data; diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c index d1092b43f..94dd23e2c 100644 --- a/src/backends/native/meta-kms-update.c +++ b/src/backends/native/meta-kms-update.c @@ -285,6 +285,7 @@ void meta_kms_update_page_flip (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, + MetaKmsPageFlipFlag flags, gpointer user_data) { MetaKmsPageFlip *page_flip; @@ -295,6 +296,7 @@ meta_kms_update_page_flip (MetaKmsUpdate *update, *page_flip = (MetaKmsPageFlip) { .crtc = crtc, .feedback = feedback, + .flags = flags, .user_data = user_data, }; diff --git a/src/backends/native/meta-kms-update.h b/src/backends/native/meta-kms-update.h index 1727c1974..0684ad181 100644 --- a/src/backends/native/meta-kms-update.h +++ b/src/backends/native/meta-kms-update.h @@ -41,6 +41,12 @@ typedef enum _MetaKmsAssignPlaneFlag META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED = 1 << 0, } MetaKmsAssignPlaneFlag; +typedef enum _MetaKmsPageFlipFlag +{ + META_KMS_PAGE_FLIP_FLAG_NONE = 0, + META_KMS_PAGE_FLIP_FLAG_NO_DISCARD_FEEDBACK = 1 << 0, +} MetaKmsPageFlipFlag; + struct _MetaKmsPageFlipFeedback { void (* flipped) (MetaKmsCrtc *crtc, @@ -99,6 +105,7 @@ MetaKmsPlaneAssignment * meta_kms_update_unassign_plane (MetaKmsUpdate *update, void meta_kms_update_page_flip (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, + MetaKmsPageFlipFlag flags, gpointer user_data); void meta_kms_update_custom_page_flip (MetaKmsUpdate *update, diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index cf29fc9b5..b183cbf0e 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -1251,10 +1251,11 @@ queue_dummy_power_save_page_flip (CoglOnscreen *onscreen) } static void -meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - MetaRendererView *view, - MetaCrtc *crtc, - MetaKmsUpdate *kms_update) +meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + MetaRendererView *view, + MetaCrtc *crtc, + MetaKmsPageFlipFlag flags, + MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; @@ -1288,6 +1289,7 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, meta_crtc_kms_assign_primary_plane (crtc_kms, fb_id, kms_update); meta_crtc_kms_page_flip (crtc_kms, &page_flip_feedback, + flags, g_object_ref (view), kms_update); @@ -1340,8 +1342,9 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen, } static void -meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen, - MetaKmsUpdate *kms_update) +meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen, + MetaKmsPageFlipFlag flags, + MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; @@ -1359,6 +1362,7 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen, if (power_save_mode == META_POWER_SAVE_ON) { meta_onscreen_native_flip_crtc (onscreen, view, onscreen_native->crtc, + flags, kms_update); } else @@ -1925,7 +1929,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed); ensure_crtc_modes (onscreen, kms_update); - meta_onscreen_native_flip_crtcs (onscreen, kms_update); + meta_onscreen_native_flip_crtcs (onscreen, + META_KMS_PAGE_FLIP_FLAG_NONE, + kms_update); /* * If we changed EGL context, cogl will have the wrong idea about what is @@ -2072,10 +2078,11 @@ meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, return TRUE; } -static void -meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, - CoglScanout *scanout, - CoglFrameInfo *frame_info) +static gboolean +meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, + CoglScanout *scanout, + CoglFrameInfo *frame_info, + GError **error) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; @@ -2090,21 +2097,38 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaKmsUpdate *kms_update; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; kms_update = meta_kms_ensure_pending_update (kms); renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, render_gpu); - g_return_if_fail (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_GBM); - + g_warn_if_fail (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_GBM); g_warn_if_fail (!onscreen_native->gbm.next_fb); + g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout)); ensure_crtc_modes (onscreen, kms_update); - meta_onscreen_native_flip_crtcs (onscreen, kms_update); + meta_onscreen_native_flip_crtcs (onscreen, + META_KMS_PAGE_FLIP_FLAG_NO_DISCARD_FEEDBACK, + kms_update); - meta_kms_post_pending_update_sync (kms); + kms_feedback = meta_kms_post_pending_update_sync (kms); + if (meta_kms_feedback_get_result (kms_feedback) != META_KMS_FEEDBACK_PASSED) + { + const GError *feedback_error = meta_kms_feedback_get_error (kms_feedback); + + if (g_error_matches (feedback_error, + G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) + return TRUE; + + g_clear_object (&onscreen_native->gbm.next_fb); + g_propagate_error (error, g_error_copy (feedback_error)); + return FALSE; + } + + return TRUE; } static gboolean