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);
}
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

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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,
};

View File

@ -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,

View File

@ -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