wayland: Emit frame callbacks when the frame is pending presentation

When Wayland clients send commits without a buffer attached ("empty"
commits), they may lead to stage updates that do not result in any
frame being submitted for presentation ("empty" updates).

Due to how frame scheduling is handled, there can be many such
"empty" updates in a single refresh cycle. If frame callbacks were
emitted after each of these "empty" updates, and if the client
sending "empty" commits was using frame callbacks to throttle the
same logic that results in these "empty" commits being sent, it would
result in a feedback loop between Mutter and the client where the
client would send "empty" commits and Mutter would reply almost
immediately with a frame callback causing the client to send "empty"
commits continuously.

As such, when an "empty" update is detected, frame callbacks are
scheduled to be emitted only once in every refresh cycle, avoiding the
feedback loop.

When a "non-empty" update is detected, frame callbacks are instead
emitted immediately to allow clients to draw their next frame as soon
as possible. It is safe to emit frame callbacks in this case because
the frame for the current refresh cycle is already "finalized" and
that any commit sent by the client at that point would only be handled
in a future refresh cycle.

To implement this, the previous logic had used
meta_frame_native_had_kms_update() to detect "non-empty" updates,
assuming that those would always result in a KMS presentation with the
native backend.

However, this approach misses the fact that virtual monitors do not
use KMS, and as such do not result in KMS presentation even for
"non-empty" updates. As a result, frame callbacks would not be emitted
immediately, resulting in unintended throttling of client rendering.

Instead, assume that it is safe to emit frame callbacks immediately
whenever an update results in the frame clock waiting to be notified
of presentation, since this is also when commits sent by clients are
scheduled to be handled in a future refresh cycle.

This issue was mostly hidden because frame callbacks would be sent
immediately when the target presentation time for the frame had
changed compared to the previous frame. However, this behavior was
removed in 26d8b9c69 ("wayland: Remove unnecessary dispatch of frame
callback source"), exposing the issue.

Fixes: a7a7933e0 ("wayland: Emit frame events in GSource after "empty" updates")
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3263
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3549>
This commit is contained in:
Dor Askayo 2024-01-26 17:08:27 +00:00
parent c9cd9cef6a
commit b8deb4caa0

View File

@ -293,7 +293,6 @@ on_after_update (ClutterStage *stage,
#if defined(HAVE_NATIVE_BACKEND)
MetaContext *context = meta_wayland_compositor_get_context (compositor);
MetaBackend *backend = meta_context_get_backend (context);
MetaFrameNative *frame_native;
GSource *source;
int64_t frame_deadline_us;
@ -303,11 +302,10 @@ on_after_update (ClutterStage *stage,
return;
}
frame_native = meta_frame_native_from_frame (frame);
source = ensure_source_for_stage_view (compositor, stage_view);
if (meta_frame_native_had_kms_update (frame_native) ||
if (clutter_frame_get_result (frame) ==
CLUTTER_FRAME_RESULT_PENDING_PRESENTED ||
!clutter_frame_get_frame_deadline (frame,
&frame_deadline_us))
{