wayland: Drive frame callbacks from stage updates

Don't tie frame callbacks to actor painting, as it may end up in
situations where we miss sending frame callbacks when we should have. An
example of this is when a surface is partially off screen, and then
reports damage that is fully off screen. When this happen, we are likely
not to repaint anything, thus we won't send any frame callbacks even
though it's "suitable" for rendering again, as the surface is not on a
separate workspace or fully obscured.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/817
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1152

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1218
This commit is contained in:
Jonas Ådahl 2020-04-27 15:43:19 +02:00
parent e8b09df8d2
commit 066bc5986d
14 changed files with 123 additions and 150 deletions

View File

@ -42,7 +42,6 @@ struct _MetaSurfaceActorWayland
MetaSurfaceActor parent;
MetaWaylandSurface *surface;
struct wl_list frame_callback_list;
};
G_DEFINE_TYPE (MetaSurfaceActorWayland,
@ -72,23 +71,6 @@ meta_surface_actor_wayland_is_opaque (MetaSurfaceActor *actor)
return meta_shaped_texture_is_opaque (stex);
}
static void
queue_frame_callbacks (MetaSurfaceActorWayland *self)
{
MetaWaylandCompositor *wayland_compositor;
if (!self->surface)
return;
if (meta_surface_actor_is_obscured (META_SURFACE_ACTOR (self)))
return;
wayland_compositor = self->surface->compositor;
wl_list_insert_list (&wayland_compositor->frame_callbacks,
&self->frame_callback_list);
wl_list_init (&self->frame_callback_list);
}
CoglScanout *
meta_surface_actor_wayland_try_acquire_scanout (MetaSurfaceActorWayland *self,
CoglOnscreen *onscreen)
@ -101,35 +83,13 @@ meta_surface_actor_wayland_try_acquire_scanout (MetaSurfaceActorWayland *self,
if (!scanout)
return NULL;
queue_frame_callbacks (self);
return scanout;
}
void
meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self,
struct wl_list *frame_callbacks)
{
wl_list_insert_list (&self->frame_callback_list, frame_callbacks);
}
static void
meta_surface_actor_wayland_paint (ClutterActor *actor,
ClutterPaintContext *paint_context)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor);
queue_frame_callbacks (self);
CLUTTER_ACTOR_CLASS (meta_surface_actor_wayland_parent_class)->paint (actor,
paint_context);
}
static void
meta_surface_actor_wayland_dispose (GObject *object)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (object);
MetaWaylandFrameCallback *cb, *next;
MetaShapedTexture *stex;
stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
@ -143,9 +103,6 @@ meta_surface_actor_wayland_dispose (GObject *object)
self->surface = NULL;
}
wl_list_for_each_safe (cb, next, &self->frame_callback_list, link)
wl_resource_destroy (cb->resource);
G_OBJECT_CLASS (meta_surface_actor_wayland_parent_class)->dispose (object);
}
@ -153,11 +110,8 @@ static void
meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass)
{
MetaSurfaceActorClass *surface_actor_class = META_SURFACE_ACTOR_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
actor_class->paint = meta_surface_actor_wayland_paint;
surface_actor_class->process_damage = meta_surface_actor_wayland_process_damage;
surface_actor_class->pre_paint = meta_surface_actor_wayland_pre_paint;
surface_actor_class->is_opaque = meta_surface_actor_wayland_is_opaque;
@ -177,7 +131,6 @@ meta_surface_actor_wayland_new (MetaWaylandSurface *surface)
g_assert (meta_is_wayland_compositor ());
wl_list_init (&self->frame_callback_list);
self->surface = surface;
g_object_add_weak_pointer (G_OBJECT (self->surface),
(gpointer *) &self->surface);

View File

@ -107,9 +107,15 @@ meta_wayland_actor_surface_assigned (MetaWaylandSurfaceRole *surface_role)
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
meta_surface_actor_wayland_add_frame_callbacks (META_SURFACE_ACTOR_WAYLAND (priv->actor),
&surface->pending_frame_callback_list);
wl_list_init (&surface->pending_frame_callback_list);
if (wl_list_empty (&surface->unassigned.pending_frame_callback_list))
return;
wl_list_insert_list (priv->frame_callback_list.prev,
&surface->unassigned.pending_frame_callback_list);
wl_list_init (&surface->unassigned.pending_frame_callback_list);
meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
surface);
}
void
@ -118,18 +124,37 @@ meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor
{
MetaWaylandActorSurfacePrivate *priv =
meta_wayland_actor_surface_get_instance_private (actor_surface);
MetaSurfaceActorWayland *surface_actor_wayland =
META_SURFACE_ACTOR_WAYLAND (priv->actor);
MetaWaylandSurfaceRole *surface_role =
META_WAYLAND_SURFACE_ROLE (actor_surface);
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
if (!priv->actor)
if (wl_list_empty (&pending->frame_callback_list))
return;
meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland,
&priv->frame_callback_list);
wl_list_init (&priv->frame_callback_list);
meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland,
wl_list_insert_list (priv->frame_callback_list.prev,
&pending->frame_callback_list);
wl_list_init (&pending->frame_callback_list);
meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
surface);
}
void
meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface,
uint32_t timestamp_ms)
{
MetaWaylandActorSurfacePrivate *priv =
meta_wayland_actor_surface_get_instance_private (actor_surface);
while (!wl_list_empty (&priv->frame_callback_list))
{
MetaWaylandFrameCallback *callback =
wl_container_of (priv->frame_callback_list.next, callback, link);
wl_callback_send_done (callback->resource, timestamp_ms);
wl_resource_destroy (callback->resource);
}
}
double
@ -265,18 +290,17 @@ meta_wayland_actor_surface_apply_state (MetaWaylandSurfaceRole *surface_role,
MetaWaylandActorSurfacePrivate *priv =
meta_wayland_actor_surface_get_instance_private (actor_surface);
if (!priv->actor)
{
wl_list_insert_list (&priv->frame_callback_list,
&pending->frame_callback_list);
wl_list_init (&pending->frame_callback_list);
return;
}
if (!wl_list_empty (&pending->frame_callback_list) &&
cairo_region_is_empty (pending->surface_damage) &&
cairo_region_is_empty (pending->buffer_damage))
clutter_actor_queue_redraw (CLUTTER_ACTOR (priv->actor));
priv->actor &&
!meta_surface_actor_is_obscured (priv->actor))
{
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
MetaBackend *backend = surface->compositor->backend;
ClutterActor *stage = meta_backend_get_stage (backend);
clutter_stage_schedule_update (CLUTTER_STAGE (stage));
}
meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);

View File

@ -48,4 +48,7 @@ void meta_wayland_actor_surface_reset_actor (MetaWaylandActorSurface *actor_surf
void meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface,
MetaWaylandSurfaceState *pending);
void meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface,
uint32_t timestamp_ms);
#endif /* META_WAYLAND_ACTOR_SURFACE_H */

View File

@ -124,8 +124,8 @@ meta_wayland_cursor_surface_assigned (MetaWaylandSurfaceRole *surface_role)
meta_wayland_cursor_surface_get_instance_private (cursor_surface);
wl_list_insert_list (&priv->frame_callbacks,
&surface->pending_frame_callback_list);
wl_list_init (&surface->pending_frame_callback_list);
&surface->unassigned.pending_frame_callback_list);
wl_list_init (&surface->unassigned.pending_frame_callback_list);
}
static void

View File

@ -23,6 +23,7 @@
#include "backends/meta-logical-monitor.h"
#include "compositor/meta-feedback-actor-private.h"
#include "wayland/meta-wayland.h"
struct _MetaWaylandSurfaceRoleDND
{
@ -42,7 +43,11 @@ dnd_surface_assigned (MetaWaylandSurfaceRole *surface_role)
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
meta_wayland_surface_queue_pending_frame_callbacks (surface);
if (wl_list_empty (&surface->unassigned.pending_frame_callback_list))
return;
meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
surface);
}
static void
@ -56,7 +61,8 @@ dnd_surface_apply_state (MetaWaylandSurfaceRole *surface_role,
MetaWaylandSurfaceRoleClass *surface_role_class =
META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_dnd_parent_class);
meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending);
meta_wayland_compositor_add_frame_callback_surface (surface->compositor,
surface);
surface_role_dnd->pending_offset_x = pending->dx;
surface_role_dnd->pending_offset_y = pending->dy;

View File

@ -658,6 +658,8 @@ meta_wayland_zxdg_toplevel_v6_apply_state (MetaWaylandSurfaceRole *surface_role
META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel);
MetaWaylandZxdgSurfaceV6Private *xdg_surface_priv =
meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface);
MetaWaylandActorSurface *actor_surface =
META_WAYLAND_ACTOR_SURFACE (xdg_surface);
MetaWaylandSurfaceRoleClass *surface_role_class;
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
@ -668,7 +670,7 @@ meta_wayland_zxdg_toplevel_v6_apply_state (MetaWaylandSurfaceRole *surface_role
window = meta_wayland_surface_get_window (surface);
if (!window)
{
meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
return;
}
@ -1218,14 +1220,10 @@ meta_wayland_zxdg_surface_v6_send_configure (MetaWaylandZxdgSurfaceV6 *xdg
static void
zxdg_surface_v6_destructor (struct wl_resource *resource)
{
MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
MetaWaylandZxdgSurfaceV6 *xdg_surface = wl_resource_get_user_data (resource);
MetaWaylandZxdgSurfaceV6Private *priv =
meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface);
meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
surface);
priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces,
xdg_surface);

View File

@ -79,7 +79,7 @@ struct _MetaWaylandCompositor
struct wl_display *wayland_display;
char *display_name;
GHashTable *outputs;
struct wl_list frame_callbacks;
GList *frame_callback_surfaces;
MetaXWaylandManager xwayland_manager;

View File

@ -362,9 +362,6 @@ wl_subsurface_destructor (struct wl_resource *resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
surface);
g_node_unlink (surface->subsurface_branch_node);
unparent_actor (surface);

View File

@ -409,15 +409,6 @@ surface_process_damage (MetaWaylandSurface *surface,
cairo_region_destroy (transformed_region);
}
void
meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface,
MetaWaylandSurfaceState *pending)
{
wl_list_insert_list (&surface->compositor->frame_callbacks,
&pending->frame_callback_list);
wl_list_init (&pending->frame_callback_list);
}
MetaWaylandBuffer *
meta_wayland_surface_get_buffer (MetaWaylandSurface *surface)
{
@ -632,15 +623,6 @@ meta_wayland_surface_state_class_init (MetaWaylandSurfaceStateClass *klass)
G_TYPE_NONE, 0);
}
void
meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface *surface,
MetaWaylandSurfaceState *pending)
{
wl_list_insert_list (&surface->pending_frame_callback_list,
&pending->frame_callback_list);
wl_list_init (&pending->frame_callback_list);
}
static void
meta_wayland_surface_apply_state (MetaWaylandSurface *surface,
MetaWaylandSurfaceState *state)
@ -776,7 +758,9 @@ meta_wayland_surface_apply_state (MetaWaylandSurface *surface,
}
else
{
meta_wayland_surface_cache_pending_frame_callbacks (surface, state);
wl_list_insert_list (surface->unassigned.pending_frame_callback_list.prev,
&state->frame_callback_list);
wl_list_init (&state->frame_callback_list);
if (state->newly_attached)
{
@ -1358,14 +1342,16 @@ wl_surface_destructor (struct wl_resource *resource)
if (surface->input_region)
cairo_region_destroy (surface->input_region);
meta_wayland_compositor_destroy_frame_callbacks (compositor, surface);
meta_wayland_compositor_remove_frame_callback_surface (compositor, surface);
g_hash_table_foreach (surface->outputs,
surface_output_disconnect_signals,
surface);
g_hash_table_destroy (surface->outputs);
wl_list_for_each_safe (cb, next, &surface->pending_frame_callback_list, link)
wl_list_for_each_safe (cb, next,
&surface->unassigned.pending_frame_callback_list,
link)
wl_resource_destroy (cb->resource);
if (surface->resource)
@ -1412,7 +1398,7 @@ meta_wayland_surface_create (MetaWaylandCompositor *compositor,
surface,
wl_surface_destructor);
wl_list_init (&surface->pending_frame_callback_list);
wl_list_init (&surface->unassigned.pending_frame_callback_list);
surface->outputs = g_hash_table_new (NULL, NULL);
surface->shortcut_inhibited_seats = g_hash_table_new (NULL, NULL);
@ -1872,14 +1858,6 @@ meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role)
return priv->surface;
}
void
meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface)
{
wl_list_insert_list (&surface->compositor->frame_callbacks,
&surface->pending_frame_callback_list);
wl_list_init (&surface->pending_frame_callback_list);
}
cairo_region_t *
meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface)
{

View File

@ -168,13 +168,9 @@ struct _MetaWaylandSurface
/* Buffer renderer state. */
gboolean buffer_held;
/* List of pending frame callbacks that needs to stay queued longer than one
* commit sequence, such as when it has not yet been assigned a role.
*/
struct wl_list pending_frame_callback_list;
/* Intermediate state for when no role has been assigned. */
struct {
struct wl_list pending_frame_callback_list;
MetaWaylandBuffer *buffer;
} unassigned;
@ -288,9 +284,6 @@ gboolean meta_wayland_surface_should_cache_state (MetaWaylandSurface
MetaWindow * meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface);
void meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface *surface,
MetaWaylandSurfaceState *pending);
void meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface);
void meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface,

View File

@ -103,9 +103,6 @@ wl_shell_surface_destructor (struct wl_resource *resource)
surface_from_wl_shell_surface_resource (resource);
GList *l;
meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
surface);
if (wl_shell_surface->popup)
meta_wayland_popup_dismiss (wl_shell_surface->popup);

View File

@ -740,6 +740,8 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role,
MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel);
MetaWaylandXdgSurfacePrivate *xdg_surface_priv =
meta_wayland_xdg_surface_get_instance_private (xdg_surface);
MetaWaylandActorSurface *actor_surface =
META_WAYLAND_ACTOR_SURFACE (xdg_toplevel);
MetaWaylandSurfaceRoleClass *surface_role_class;
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
@ -750,15 +752,12 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role,
window = meta_wayland_surface_get_window (surface);
if (!window)
{
meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
return;
}
if (!surface->buffer_ref->buffer && xdg_surface_priv->first_buffer_attached)
{
MetaWaylandActorSurface *actor_surface =
META_WAYLAND_ACTOR_SURFACE (xdg_toplevel);
meta_wayland_xdg_surface_reset (xdg_surface);
meta_wayland_actor_surface_queue_frame_callbacks (actor_surface,
pending);
@ -1089,6 +1088,8 @@ meta_wayland_xdg_popup_apply_state (MetaWaylandSurfaceRole *surface_role,
MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role);
MetaWaylandXdgSurfacePrivate *xdg_surface_priv =
meta_wayland_xdg_surface_get_instance_private (xdg_surface);
MetaWaylandActorSurface *actor_surface =
META_WAYLAND_ACTOR_SURFACE (xdg_popup);
MetaWaylandSurfaceRoleClass *surface_role_class;
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
@ -1103,7 +1104,7 @@ meta_wayland_xdg_popup_apply_state (MetaWaylandSurfaceRole *surface_role,
if (!surface->buffer_ref->buffer && xdg_surface_priv->first_buffer_attached)
{
meta_wayland_xdg_surface_reset (xdg_surface);
meta_wayland_surface_cache_pending_frame_callbacks (surface, pending);
meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
return;
}
@ -1380,14 +1381,10 @@ meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_sur
static void
xdg_surface_destructor (struct wl_resource *resource)
{
MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
MetaWaylandXdgSurfacePrivate *priv =
meta_wayland_xdg_surface_get_instance_private (xdg_surface);
meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
surface);
priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces,
xdg_surface);

View File

@ -197,15 +197,35 @@ meta_wayland_compositor_update (MetaWaylandCompositor *compositor,
void
meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor)
{
gint64 current_time = g_get_monotonic_time ();
GList *l;
int64_t now_us;
while (!wl_list_empty (&compositor->frame_callbacks))
now_us = g_get_monotonic_time ();
l = compositor->frame_callback_surfaces;
while (l)
{
MetaWaylandFrameCallback *callback =
wl_container_of (compositor->frame_callbacks.next, callback, link);
GList *l_cur = l;
MetaWaylandSurface *surface = l->data;
MetaSurfaceActor *actor;
MetaWaylandActorSurface *actor_surface;
wl_callback_send_done (callback->resource, current_time / 1000);
wl_resource_destroy (callback->resource);
l = l->next;
actor = meta_wayland_surface_get_actor (surface);
if (!actor)
continue;
if (!clutter_actor_has_mapped_clones (CLUTTER_ACTOR (actor)) &&
meta_surface_actor_is_obscured (actor))
continue;
actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role);
meta_wayland_actor_surface_emit_frame_callbacks (actor_surface,
now_us / 1000);
compositor->frame_callback_surfaces =
g_list_delete_link (compositor->frame_callback_surfaces, l_cur);
}
}
@ -252,16 +272,22 @@ meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor,
}
void
meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor,
meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface)
{
MetaWaylandFrameCallback *callback, *next;
if (g_list_find (compositor->frame_callback_surfaces, surface))
return;
wl_list_for_each_safe (callback, next, &compositor->frame_callbacks, link)
{
if (callback->surface == surface)
wl_resource_destroy (callback->resource);
compositor->frame_callback_surfaces =
g_list_prepend (compositor->frame_callback_surfaces, surface);
}
void
meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface)
{
compositor->frame_callback_surfaces =
g_list_remove (compositor->frame_callback_surfaces, surface);
}
static void
@ -313,8 +339,6 @@ meta_wayland_log_func (const char *fmt,
static void
meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
{
wl_list_init (&compositor->frame_callbacks);
compositor->scheduled_surface_associations = g_hash_table_new (NULL, NULL);
wl_log_set_handler_server (meta_wayland_log_func);

View File

@ -62,7 +62,10 @@ void meta_wayland_compositor_set_input_focus (MetaWaylandComp
void meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor);
void meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor,
void meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface);
void meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface);
META_EXPORT_TEST