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
This commit is contained in:
Jonas Ådahl 2020-09-10 17:41:06 +02:00 committed by Georges Basile Stavracas Neto
parent aa56595b31
commit 6e3ecadb79
11 changed files with 103 additions and 39 deletions

View File

@ -649,10 +649,11 @@ clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
cairo_region_destroy (swap_region); cairo_region_destroy (swap_region);
} }
static void static gboolean
clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl, clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl,
ClutterStageView *view, ClutterStageView *view,
CoglScanout *scanout) CoglScanout *scanout,
GError **error)
{ {
ClutterStageCoglPrivate *priv = ClutterStageCoglPrivate *priv =
_clutter_stage_cogl_get_instance_private (stage_cogl); _clutter_stage_cogl_get_instance_private (stage_cogl);
@ -660,14 +661,21 @@ clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl,
CoglOnscreen *onscreen; CoglOnscreen *onscreen;
CoglFrameInfo *frame_info; CoglFrameInfo *frame_info;
g_return_if_fail (cogl_is_onscreen (framebuffer)); g_assert (cogl_is_onscreen (framebuffer));
onscreen = COGL_ONSCREEN (framebuffer); onscreen = COGL_ONSCREEN (framebuffer);
frame_info = cogl_frame_info_new (priv->global_frame_counter); 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++; priv->global_frame_counter++;
cogl_onscreen_direct_scanout (onscreen, scanout, frame_info); return TRUE;
} }
static void static void
@ -679,8 +687,15 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
scanout = clutter_stage_view_take_scanout (view); scanout = clutter_stage_view_take_scanout (view);
if (scanout) if (scanout)
clutter_stage_cogl_scanout_view (stage_cogl, view, scanout); {
else 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); clutter_stage_cogl_redraw_view_primary (stage_cogl, view);
} }

View File

@ -406,24 +406,30 @@ cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen)
return winsys->onscreen_get_buffer_age (onscreen); return winsys->onscreen_get_buffer_age (onscreen);
} }
void gboolean
cogl_onscreen_direct_scanout (CoglOnscreen *onscreen, cogl_onscreen_direct_scanout (CoglOnscreen *onscreen,
CoglScanout *scanout, CoglScanout *scanout,
CoglFrameInfo *info) CoglFrameInfo *info,
GError **error)
{ {
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
const CoglWinsysVtable *winsys; const CoglWinsysVtable *winsys;
g_return_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); g_warn_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 (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT));
info->frame_counter = onscreen->frame_counter; info->frame_counter = onscreen->frame_counter;
g_queue_push_tail (&onscreen->pending_frame_infos, info); g_queue_push_tail (&onscreen->pending_frame_infos, info);
winsys = _cogl_framebuffer_get_winsys (framebuffer); 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++; onscreen->frame_counter++;
return TRUE;
} }
#ifdef COGL_HAS_X11_SUPPORT #ifdef COGL_HAS_X11_SUPPORT

View File

@ -291,10 +291,11 @@ cogl_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
/** /**
* cogl_onscreen_direct_scanout: (skip) * cogl_onscreen_direct_scanout: (skip)
*/ */
COGL_EXPORT void COGL_EXPORT gboolean
cogl_onscreen_direct_scanout (CoglOnscreen *onscreen, cogl_onscreen_direct_scanout (CoglOnscreen *onscreen,
CoglScanout *scanout, CoglScanout *scanout,
CoglFrameInfo *info); CoglFrameInfo *info,
GError **error);
/** /**
* cogl_onscreen_swap_region: * cogl_onscreen_swap_region:

View File

@ -119,10 +119,11 @@ typedef struct _CoglWinsysVtable
int n_rectangles, int n_rectangles,
CoglFrameInfo *info); CoglFrameInfo *info);
void gboolean
(*onscreen_direct_scanout) (CoglOnscreen *onscreen, (*onscreen_direct_scanout) (CoglOnscreen *onscreen,
CoglScanout *scanout, CoglScanout *scanout,
CoglFrameInfo *info); CoglFrameInfo *info,
GError **error);
void void
(*onscreen_set_visibility) (CoglOnscreen *onscreen, (*onscreen_set_visibility) (CoglOnscreen *onscreen,

View File

@ -206,12 +206,14 @@ meta_crtc_kms_set_mode (MetaCrtcKms *crtc_kms,
void void
meta_crtc_kms_page_flip (MetaCrtcKms *crtc_kms, meta_crtc_kms_page_flip (MetaCrtcKms *crtc_kms,
const MetaKmsPageFlipFeedback *page_flip_feedback, const MetaKmsPageFlipFeedback *page_flip_feedback,
MetaKmsPageFlipFlag flags,
gpointer user_data, gpointer user_data,
MetaKmsUpdate *kms_update) MetaKmsUpdate *kms_update)
{ {
meta_kms_update_page_flip (kms_update, meta_kms_update_page_flip (kms_update,
meta_crtc_kms_get_kms_crtc (crtc_kms), meta_crtc_kms_get_kms_crtc (crtc_kms),
page_flip_feedback, page_flip_feedback,
flags,
user_data); user_data);
} }

View File

@ -30,6 +30,7 @@
#include "backends/meta-crtc.h" #include "backends/meta-crtc.h"
#include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-gpu-kms.h"
#include "backends/native/meta-kms-crtc.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 ()) #define META_TYPE_CRTC_KMS (meta_crtc_kms_get_type ())
G_DECLARE_FINAL_TYPE (MetaCrtcKms, meta_crtc_kms, 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, void meta_crtc_kms_page_flip (MetaCrtcKms *crtc_kms,
const MetaKmsPageFlipFeedback *page_flip_feedback, const MetaKmsPageFlipFeedback *page_flip_feedback,
MetaKmsPageFlipFlag flags,
gpointer user_data, gpointer user_data,
MetaKmsUpdate *kms_update); MetaKmsUpdate *kms_update);

View File

@ -988,6 +988,9 @@ err_planes_assigned:
{ {
MetaKmsPageFlip *page_flip = l->data; 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); discard_page_flip (impl, update, page_flip);
} }

View File

@ -87,6 +87,7 @@ typedef struct _MetaKmsPageFlip
{ {
MetaKmsCrtc *crtc; MetaKmsCrtc *crtc;
const MetaKmsPageFlipFeedback *feedback; const MetaKmsPageFlipFeedback *feedback;
MetaKmsPageFlipFlag flags;
gpointer user_data; gpointer user_data;
MetaKmsCustomPageFlipFunc custom_page_flip_func; MetaKmsCustomPageFlipFunc custom_page_flip_func;
gpointer custom_page_flip_user_data; gpointer custom_page_flip_user_data;

View File

@ -285,6 +285,7 @@ void
meta_kms_update_page_flip (MetaKmsUpdate *update, meta_kms_update_page_flip (MetaKmsUpdate *update,
MetaKmsCrtc *crtc, MetaKmsCrtc *crtc,
const MetaKmsPageFlipFeedback *feedback, const MetaKmsPageFlipFeedback *feedback,
MetaKmsPageFlipFlag flags,
gpointer user_data) gpointer user_data)
{ {
MetaKmsPageFlip *page_flip; MetaKmsPageFlip *page_flip;
@ -295,6 +296,7 @@ meta_kms_update_page_flip (MetaKmsUpdate *update,
*page_flip = (MetaKmsPageFlip) { *page_flip = (MetaKmsPageFlip) {
.crtc = crtc, .crtc = crtc,
.feedback = feedback, .feedback = feedback,
.flags = flags,
.user_data = user_data, .user_data = user_data,
}; };

View File

@ -41,6 +41,12 @@ typedef enum _MetaKmsAssignPlaneFlag
META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED = 1 << 0, META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED = 1 << 0,
} MetaKmsAssignPlaneFlag; } MetaKmsAssignPlaneFlag;
typedef enum _MetaKmsPageFlipFlag
{
META_KMS_PAGE_FLIP_FLAG_NONE = 0,
META_KMS_PAGE_FLIP_FLAG_NO_DISCARD_FEEDBACK = 1 << 0,
} MetaKmsPageFlipFlag;
struct _MetaKmsPageFlipFeedback struct _MetaKmsPageFlipFeedback
{ {
void (* flipped) (MetaKmsCrtc *crtc, void (* flipped) (MetaKmsCrtc *crtc,
@ -99,6 +105,7 @@ MetaKmsPlaneAssignment * meta_kms_update_unassign_plane (MetaKmsUpdate *update,
void meta_kms_update_page_flip (MetaKmsUpdate *update, void meta_kms_update_page_flip (MetaKmsUpdate *update,
MetaKmsCrtc *crtc, MetaKmsCrtc *crtc,
const MetaKmsPageFlipFeedback *feedback, const MetaKmsPageFlipFeedback *feedback,
MetaKmsPageFlipFlag flags,
gpointer user_data); gpointer user_data);
void meta_kms_update_custom_page_flip (MetaKmsUpdate *update, void meta_kms_update_custom_page_flip (MetaKmsUpdate *update,

View File

@ -1254,6 +1254,7 @@ static void
meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
MetaRendererView *view, MetaRendererView *view,
MetaCrtc *crtc, MetaCrtc *crtc,
MetaKmsPageFlipFlag flags,
MetaKmsUpdate *kms_update) MetaKmsUpdate *kms_update)
{ {
CoglOnscreenEGL *onscreen_egl = onscreen->winsys; CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
@ -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_assign_primary_plane (crtc_kms, fb_id, kms_update);
meta_crtc_kms_page_flip (crtc_kms, meta_crtc_kms_page_flip (crtc_kms,
&page_flip_feedback, &page_flip_feedback,
flags,
g_object_ref (view), g_object_ref (view),
kms_update); kms_update);
@ -1341,6 +1343,7 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen,
static void static void
meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen, meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen,
MetaKmsPageFlipFlag flags,
MetaKmsUpdate *kms_update) MetaKmsUpdate *kms_update)
{ {
CoglOnscreenEGL *onscreen_egl = onscreen->winsys; CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
@ -1359,6 +1362,7 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen,
if (power_save_mode == META_POWER_SAVE_ON) if (power_save_mode == META_POWER_SAVE_ON)
{ {
meta_onscreen_native_flip_crtc (onscreen, view, onscreen_native->crtc, meta_onscreen_native_flip_crtc (onscreen, view, onscreen_native->crtc,
flags,
kms_update); kms_update);
} }
else 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); update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
ensure_crtc_modes (onscreen, kms_update); 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 * 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; return TRUE;
} }
static void static gboolean
meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
CoglScanout *scanout, CoglScanout *scanout,
CoglFrameInfo *frame_info) CoglFrameInfo *frame_info,
GError **error)
{ {
CoglOnscreenEGL *onscreen_egl = onscreen->winsys; CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
@ -2090,21 +2097,38 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaKms *kms = meta_backend_native_get_kms (backend_native);
MetaKmsUpdate *kms_update; MetaKmsUpdate *kms_update;
g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
kms_update = meta_kms_ensure_pending_update (kms); kms_update = meta_kms_ensure_pending_update (kms);
renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
render_gpu); 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_warn_if_fail (!onscreen_native->gbm.next_fb);
g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout)); g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout));
ensure_crtc_modes (onscreen, kms_update); 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 static gboolean