onscreen/native: Handle unexpected scanout failures async
We test direct client buffer scanout using a TEST_ONLY commit on atomic, and with various conditions in non-atomic, but if we end up failing to actually commit despite this, handle the fallout asynchronously. What this means is that we'll reschedule a new frame immediately. For this to work, the same scanout buffer needs to be avoided for the same CRTC. This is done by using the newly added signal on the CoglScanout object to let the MetaWaylandBuffer object mark the current buffer as non-working for the onsrceen that it failed on. This allows to re-try buffers on the same onscreen when new ones are attached. This queues a full damage, since we consumed the qeued redraw rect. The redraw rect wasn't lost - it was accumulated to make sure the whole primary plane was redrawed according to the damage region, whenever we would end up no longer doing direct scanout, but this accumulation only works when we're not intentionally stopping to scanout. For now, lets just damage the whole view, it's just an graceful fallback in response to an unexpected error anyway. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2854>
This commit is contained in:
parent
71b787f169
commit
02b1cfe08f
10 changed files with 126 additions and 104 deletions
|
@ -1046,17 +1046,14 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device,
|
|||
err:
|
||||
meta_topic (META_DEBUG_KMS, "[atomic] KMS update failed: %s", error->message);
|
||||
|
||||
if (!(flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR))
|
||||
{
|
||||
process_entries (impl_device,
|
||||
update,
|
||||
req,
|
||||
blob_ids,
|
||||
meta_kms_update_get_page_flip_listeners (update),
|
||||
error,
|
||||
discard_page_flip_listener,
|
||||
NULL);
|
||||
}
|
||||
process_entries (impl_device,
|
||||
update,
|
||||
req,
|
||||
blob_ids,
|
||||
meta_kms_update_get_page_flip_listeners (update),
|
||||
error,
|
||||
discard_page_flip_listener,
|
||||
NULL);
|
||||
|
||||
if (req)
|
||||
drmModeAtomicFree (req);
|
||||
|
|
|
@ -1166,8 +1166,7 @@ maybe_dispatch_page_flips (MetaKmsImplDevice *impl_device,
|
|||
*failed_planes = g_list_prepend (*failed_planes, plane_feedback);
|
||||
}
|
||||
|
||||
if (!(flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR))
|
||||
meta_kms_page_flip_data_discard_in_impl (page_flip_data, *error);
|
||||
meta_kms_page_flip_data_discard_in_impl (page_flip_data, *error);
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
@ -1176,14 +1175,11 @@ maybe_dispatch_page_flips (MetaKmsImplDevice *impl_device,
|
|||
return TRUE;
|
||||
|
||||
err:
|
||||
if (!(flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR))
|
||||
for (l = page_flip_datas; l; l = l->next)
|
||||
{
|
||||
for (l = page_flip_datas; l; l = l->next)
|
||||
{
|
||||
MetaKmsPageFlipData *page_flip_data = l->data;
|
||||
MetaKmsPageFlipData *page_flip_data = l->data;
|
||||
|
||||
meta_kms_page_flip_data_discard_in_impl (page_flip_data, *error);
|
||||
}
|
||||
meta_kms_page_flip_data_discard_in_impl (page_flip_data, *error);
|
||||
}
|
||||
g_list_free (page_flip_datas);
|
||||
|
||||
|
|
|
@ -79,8 +79,7 @@ typedef enum _MetaKmsResourceChanges
|
|||
typedef enum _MetaKmsUpdateFlag
|
||||
{
|
||||
META_KMS_UPDATE_FLAG_NONE = 0,
|
||||
META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR = 1 << 0,
|
||||
META_KMS_UPDATE_FLAG_TEST_ONLY = 1 << 1,
|
||||
META_KMS_UPDATE_FLAG_TEST_ONLY = 1 << 0,
|
||||
} MetaKmsUpdateFlag;
|
||||
|
||||
typedef enum _MetaKmsPlaneType MetaKmsPlaneType;
|
||||
|
|
|
@ -170,8 +170,6 @@ GList * meta_kms_update_get_mode_sets (MetaKmsUpdate *update);
|
|||
META_EXPORT_TEST
|
||||
GList * meta_kms_update_get_page_flip_listeners (MetaKmsUpdate *update);
|
||||
|
||||
void meta_kms_update_drop_defunct_page_flip_listeners (MetaKmsUpdate *update);
|
||||
|
||||
META_EXPORT_TEST
|
||||
GList * meta_kms_update_get_connector_updates (MetaKmsUpdate *update);
|
||||
|
||||
|
|
|
@ -462,28 +462,6 @@ meta_kms_update_add_page_flip_listener (MetaKmsUpdate *upd
|
|||
listener);
|
||||
}
|
||||
|
||||
void
|
||||
meta_kms_update_drop_defunct_page_flip_listeners (MetaKmsUpdate *update)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
l = update->page_flip_listeners;
|
||||
while (l)
|
||||
{
|
||||
MetaKmsPageFlipListener *listener = l->data;
|
||||
GList *l_next = l->next;
|
||||
|
||||
if (listener->flags & META_KMS_PAGE_FLIP_LISTENER_FLAG_DROP_ON_ERROR)
|
||||
{
|
||||
meta_kms_page_flip_listener_unref (listener);
|
||||
update->page_flip_listeners =
|
||||
g_list_delete_link (update->page_flip_listeners, l);
|
||||
}
|
||||
|
||||
l = l_next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_kms_update_set_custom_page_flip (MetaKmsUpdate *update,
|
||||
MetaKmsCustomPageFlipFunc func,
|
||||
|
|
|
@ -46,7 +46,6 @@ typedef enum _MetaKmsAssignPlaneFlag
|
|||
enum _MetaKmsPageFlipListenerFlag
|
||||
{
|
||||
META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE = 0,
|
||||
META_KMS_PAGE_FLIP_LISTENER_FLAG_DROP_ON_ERROR = 1 << 0,
|
||||
};
|
||||
|
||||
struct _MetaKmsPageFlipListenerVtable
|
||||
|
|
|
@ -270,28 +270,7 @@ meta_kms_post_pending_update_sync (MetaKms *kms,
|
|||
|
||||
result_listeners = meta_kms_update_take_result_listeners (update);
|
||||
|
||||
if (feedback->error &&
|
||||
flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
meta_kms_update_unlock (update);
|
||||
|
||||
for (l = feedback->failed_planes; l; l = l->next)
|
||||
{
|
||||
MetaKmsPlane *plane = l->data;
|
||||
|
||||
meta_kms_update_drop_plane_assignment (update, plane);
|
||||
}
|
||||
|
||||
meta_kms_update_drop_defunct_page_flip_listeners (update);
|
||||
|
||||
meta_kms_add_pending_update (kms, update);
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_kms_update_free (update);
|
||||
}
|
||||
meta_kms_update_free (update);
|
||||
|
||||
for (l = result_listeners; l; l = l->next)
|
||||
{
|
||||
|
|
|
@ -1266,6 +1266,40 @@ meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen,
|
|||
return result == META_KMS_FEEDBACK_PASSED;
|
||||
}
|
||||
|
||||
static void
|
||||
on_scanout_update_result (const MetaKmsFeedback *kms_feedback,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (user_data);
|
||||
CoglOnscreen *onscreen = COGL_ONSCREEN (onscreen_native);
|
||||
const GError *error;
|
||||
CoglFrameInfo *frame_info;
|
||||
|
||||
error = meta_kms_feedback_get_error (kms_feedback);
|
||||
if (!error)
|
||||
return;
|
||||
|
||||
if (!g_error_matches (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_PERMISSION_DENIED))
|
||||
{
|
||||
ClutterStageView *view = CLUTTER_STAGE_VIEW (onscreen_native->view);
|
||||
|
||||
g_warning ("Direct scanout page flip failed: %s", error->message);
|
||||
|
||||
cogl_scanout_notify_failed (COGL_SCANOUT (onscreen_native->gbm.next_fb),
|
||||
onscreen);
|
||||
clutter_stage_view_add_redraw_clip (view, NULL);
|
||||
clutter_stage_view_schedule_update_now (view);
|
||||
}
|
||||
|
||||
frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
|
||||
frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC;
|
||||
|
||||
meta_onscreen_native_notify_frame_complete (onscreen);
|
||||
meta_onscreen_native_clear_next_fb (onscreen);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
|
||||
CoglScanout *scanout,
|
||||
|
@ -1293,9 +1327,7 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
|
|||
GError *fill_timings_error = NULL;
|
||||
MetaKmsCrtc *kms_crtc;
|
||||
MetaKmsDevice *kms_device;
|
||||
MetaKmsUpdateFlag flags;
|
||||
g_autoptr (MetaKmsFeedback) kms_feedback = NULL;
|
||||
const GError *feedback_error;
|
||||
MetaKmsUpdate *kms_update;
|
||||
|
||||
power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager);
|
||||
if (power_save_mode != META_POWER_SAVE_ON)
|
||||
|
@ -1351,40 +1383,28 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen,
|
|||
}
|
||||
}
|
||||
|
||||
kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc));
|
||||
kms_device = meta_kms_crtc_get_device (kms_crtc);
|
||||
kms_update = meta_kms_ensure_pending_update (kms, kms_device);
|
||||
|
||||
meta_kms_update_add_result_listener (kms_update,
|
||||
on_scanout_update_result,
|
||||
onscreen_native);
|
||||
|
||||
meta_onscreen_native_flip_crtc (onscreen,
|
||||
onscreen_native->view,
|
||||
onscreen_native->crtc,
|
||||
META_KMS_PAGE_FLIP_LISTENER_FLAG_DROP_ON_ERROR,
|
||||
META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc));
|
||||
kms_device = meta_kms_crtc_get_device (kms_crtc);
|
||||
|
||||
meta_topic (META_DEBUG_KMS,
|
||||
"Posting direct scanout update for CRTC %u (%s)",
|
||||
meta_kms_crtc_get_id (kms_crtc),
|
||||
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);
|
||||
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:
|
||||
feedback_error = meta_kms_feedback_get_error (kms_feedback);
|
||||
|
||||
if (g_error_matches (feedback_error,
|
||||
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;
|
||||
}
|
||||
clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);
|
||||
meta_kms_post_pending_update_sync (kms, kms_device, META_KMS_UPDATE_FLAG_NONE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -547,6 +547,43 @@ egl_stream_buffer_attach (MetaWaylandBuffer *buffer,
|
|||
}
|
||||
#endif /* HAVE_WAYLAND_EGLSTREAM */
|
||||
|
||||
static void
|
||||
on_onscreen_destroyed (gpointer user_data,
|
||||
GObject *where_the_onscreen_was)
|
||||
{
|
||||
MetaWaylandBuffer *buffer = user_data;
|
||||
|
||||
g_hash_table_remove (buffer->tainted_scanout_onscreens,
|
||||
where_the_onscreen_was);
|
||||
}
|
||||
|
||||
static void
|
||||
on_scanout_failed (CoglScanout *scanout,
|
||||
CoglOnscreen *onscreen,
|
||||
MetaWaylandBuffer *buffer)
|
||||
{
|
||||
if (!buffer->tainted_scanout_onscreens)
|
||||
buffer->tainted_scanout_onscreens = g_hash_table_new (NULL, NULL);
|
||||
|
||||
g_hash_table_add (buffer->tainted_scanout_onscreens, onscreen);
|
||||
g_object_weak_ref (G_OBJECT (onscreen), on_onscreen_destroyed, buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_tainted_scanout_onscreens (MetaWaylandBuffer *buffer)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
CoglOnscreen *onscreen;
|
||||
|
||||
if (!buffer->tainted_scanout_onscreens)
|
||||
return;
|
||||
|
||||
g_hash_table_iter_init (&iter, buffer->tainted_scanout_onscreens);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &onscreen, NULL))
|
||||
g_object_weak_unref (G_OBJECT (onscreen), on_onscreen_destroyed, buffer);
|
||||
g_hash_table_remove_all (buffer->tainted_scanout_onscreens);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_wayland_buffer_attach:
|
||||
* @buffer: a pointer to a #MetaWaylandBuffer
|
||||
|
@ -573,6 +610,8 @@ meta_wayland_buffer_attach (MetaWaylandBuffer *buffer,
|
|||
{
|
||||
COGL_TRACE_BEGIN_SCOPED (MetaWaylandBufferAttach, "WaylandBuffer (attach)");
|
||||
|
||||
clear_tainted_scanout_onscreens (buffer);
|
||||
|
||||
if (!meta_wayland_buffer_is_realized (buffer))
|
||||
{
|
||||
/* The buffer should have been realized at surface commit time */
|
||||
|
@ -797,24 +836,31 @@ CoglScanout *
|
|||
meta_wayland_buffer_try_acquire_scanout (MetaWaylandBuffer *buffer,
|
||||
CoglOnscreen *onscreen)
|
||||
{
|
||||
CoglScanout *scanout = NULL;
|
||||
|
||||
COGL_TRACE_BEGIN_SCOPED (MetaWaylandBufferTryScanout,
|
||||
"WaylandBuffer (try scanout)");
|
||||
|
||||
if (buffer->tainted_scanout_onscreens &&
|
||||
g_hash_table_lookup (buffer->tainted_scanout_onscreens, onscreen))
|
||||
{
|
||||
meta_topic (META_DEBUG_RENDER, "Buffer scanout capability tainted");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (buffer->type)
|
||||
{
|
||||
case META_WAYLAND_BUFFER_TYPE_SHM:
|
||||
case META_WAYLAND_BUFFER_TYPE_SINGLE_PIXEL:
|
||||
#ifdef HAVE_WAYLAND_EGLSTREAM
|
||||
case META_WAYLAND_BUFFER_TYPE_EGL_STREAM:
|
||||
#endif
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Buffer type not scanout compatible");
|
||||
return NULL;
|
||||
case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE:
|
||||
return try_acquire_egl_image_scanout (buffer, onscreen);
|
||||
#ifdef HAVE_WAYLAND_EGLSTREAM
|
||||
case META_WAYLAND_BUFFER_TYPE_EGL_STREAM:
|
||||
meta_topic (META_DEBUG_RENDER,
|
||||
"Buffer type not scanout compatible");
|
||||
return NULL;
|
||||
#endif
|
||||
scanout = try_acquire_egl_image_scanout (buffer, onscreen);
|
||||
break;
|
||||
case META_WAYLAND_BUFFER_TYPE_DMA_BUF:
|
||||
{
|
||||
MetaWaylandDmaBufBuffer *dma_buf;
|
||||
|
@ -823,15 +869,20 @@ meta_wayland_buffer_try_acquire_scanout (MetaWaylandBuffer *buffer,
|
|||
if (!dma_buf)
|
||||
return NULL;
|
||||
|
||||
return meta_wayland_dma_buf_try_acquire_scanout (dma_buf, onscreen);
|
||||
scanout = meta_wayland_dma_buf_try_acquire_scanout (dma_buf, onscreen);
|
||||
break;
|
||||
}
|
||||
case META_WAYLAND_BUFFER_TYPE_UNKNOWN:
|
||||
g_warn_if_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
g_return_val_if_fail (scanout, NULL);
|
||||
|
||||
g_signal_connect (scanout, "scanout-failed",
|
||||
G_CALLBACK (on_scanout_failed), buffer);
|
||||
|
||||
return scanout;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -841,6 +892,9 @@ meta_wayland_buffer_finalize (GObject *object)
|
|||
|
||||
g_warn_if_fail (buffer->use_count == 0);
|
||||
|
||||
clear_tainted_scanout_onscreens (buffer);
|
||||
g_clear_pointer (&buffer->tainted_scanout_onscreens, g_hash_table_unref);
|
||||
|
||||
g_clear_pointer (&buffer->egl_image.texture, cogl_object_unref);
|
||||
#ifdef HAVE_WAYLAND_EGLSTREAM
|
||||
g_clear_pointer (&buffer->egl_stream.texture, cogl_object_unref);
|
||||
|
|
|
@ -80,6 +80,8 @@ struct _MetaWaylandBuffer
|
|||
MetaWaylandSinglePixelBuffer *single_pixel_buffer;
|
||||
CoglTexture *texture;
|
||||
} single_pixel;
|
||||
|
||||
GHashTable *tainted_scanout_onscreens;
|
||||
};
|
||||
|
||||
#define META_TYPE_WAYLAND_BUFFER (meta_wayland_buffer_get_type ())
|
||||
|
|
Loading…
Reference in a new issue