mutter/0002-stage-Track-overlay-damage-per-view.patch
Jonas Ådahl efbeeeb09a Backport fix for software cursors artifacts
Resolves: RHEL-58079
Resolves: RHEL-81897
2025-03-12 23:36:12 +08:00

267 lines
8.5 KiB
Diff

From 3e09a7f1717d8c4b0a815fd9673471a52d6328b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Mar 2025 11:56:46 +0800
Subject: [PATCH 2/2] stage: Track overlay damage per view
The previously painted rectangle of an overlay is not a global state,
but depends on what view it was painted on. There was also an issue
where an overlay being updated but not changing position, e.g. due to a
0,0 pointer movement or an absolute pointer movement with the position
not changing, not properly triggering damage of the old position when it
eventually actually moved.
Fix this by tracking damage per view, while also fixing the state
tracking to handle unchanged positions.
---
src/backends/meta-stage.c | 163 +++++++++++++++++++++++++-------------
1 file changed, 107 insertions(+), 56 deletions(-)
diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c
index 32f681c8c5..58f15bbb33 100644
--- a/src/backends/meta-stage.c
+++ b/src/backends/meta-stage.c
@@ -38,6 +38,12 @@ struct _MetaStageWatch
gpointer user_data;
};
+typedef struct _MetaOverlayViewState
+{
+ graphene_rect_t painted_rect;
+ gboolean has_painted_rect;
+} MetaOverlayViewState;
+
struct _MetaOverlay
{
MetaStage *stage;
@@ -49,8 +55,8 @@ struct _MetaOverlay
graphene_matrix_t transform;
graphene_rect_t current_rect;
- graphene_rect_t previous_rect;
- gboolean previous_is_valid;
+
+ GHashTable *view_states;
};
struct _MetaStage
@@ -77,6 +83,7 @@ meta_overlay_new (MetaStage *stage)
overlay = g_new0 (MetaOverlay, 1);
overlay->stage = stage;
overlay->pipeline = cogl_pipeline_new (ctx);
+ overlay->view_states = g_hash_table_new_full (NULL, NULL, NULL, g_free);
return overlay;
}
@@ -86,6 +93,7 @@ meta_overlay_free (MetaOverlay *overlay)
{
if (overlay->pipeline)
g_object_unref (overlay->pipeline);
+ g_hash_table_unref (overlay->view_states);
g_free (overlay);
}
@@ -115,19 +123,57 @@ meta_overlay_set (MetaOverlay *overlay,
overlay->current_rect = *dst_rect;
}
+static MetaOverlayViewState *
+get_view_state (MetaOverlay *overlay,
+ ClutterStageView *view)
+{
+ return g_hash_table_lookup (overlay->view_states, view);
+}
+
+static MetaOverlayViewState *
+ensure_view_state (MetaOverlay *overlay,
+ ClutterStageView *view)
+{
+ MetaOverlayViewState *view_state;
+
+ view_state = get_view_state (overlay, view);
+
+ if (!view_state)
+ {
+ view_state = g_new0 (MetaOverlayViewState, 1);
+ g_hash_table_insert (overlay->view_states, view, view_state);
+ }
+
+ return view_state;
+}
+
+static void
+meta_overlay_invalidate_views (MetaOverlay *overlay)
+{
+ g_hash_table_remove_all (overlay->view_states);
+}
+
static void
meta_overlay_paint (MetaOverlay *overlay,
ClutterPaintContext *paint_context)
{
+ ClutterStageView *view;
+ MetaOverlayViewState *view_state = NULL;
CoglFramebuffer *framebuffer;
- if (!overlay->texture)
- return;
+ view = clutter_paint_context_get_stage_view (paint_context);
+ if (view)
+ view_state = ensure_view_state (overlay, view);
- if (!overlay->is_visible &&
+ if ((!overlay->texture ||
+ !overlay->is_visible) &&
!(clutter_paint_context_get_paint_flags (paint_context) &
CLUTTER_PAINT_FLAG_FORCE_CURSORS))
- return;
+ {
+ if (view_state)
+ view_state->has_painted_rect = FALSE;
+ return;
+ }
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
cogl_framebuffer_draw_rectangle (framebuffer,
@@ -139,10 +185,10 @@ meta_overlay_paint (MetaOverlay *overlay,
(overlay->current_rect.origin.y +
overlay->current_rect.size.height));
- if (!graphene_rect_equal (&overlay->previous_rect, &overlay->current_rect))
+ if (view_state)
{
- overlay->previous_rect = overlay->current_rect;
- overlay->previous_is_valid = TRUE;
+ view_state->painted_rect = overlay->current_rect;
+ view_state->has_painted_rect = TRUE;
}
}
@@ -343,67 +389,68 @@ meta_stage_new (MetaBackend *backend)
}
static void
-queue_cursor_overlay_redraw_clutter_rect (MetaStage *stage,
- MetaOverlay *overlay,
- graphene_rect_t *rect)
+intersect_and_queue_redraw (ClutterStageView *view,
+ const MtkRectangle *clip)
{
- MtkRectangle clip = {
- .x = (int) floorf (rect->origin.x),
- .y = (int) floorf (rect->origin.y),
- .width = (int) ceilf (rect->size.width),
- .height = (int) ceilf (rect->size.height)
- };
- GList *l;
+ MtkRectangle view_layout;
+ MtkRectangle view_clip;
- /* Since we're flooring the coordinates, we need to enlarge the clip by the
- * difference between the actual coordinate and the floored value */
- clip.width += (int) ceilf (rect->origin.x - clip.x) * 2;
- clip.height += (int) ceilf (rect->origin.y - clip.y) * 2;
+ clutter_stage_view_get_layout (view, &view_layout);
- for (l = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
- l;
- l = l->next)
+ if (mtk_rectangle_intersect (clip, &view_layout, &view_clip))
{
- ClutterStageView *view = l->data;
- MtkRectangle view_layout;
- MtkRectangle view_clip;
-
- if (clutter_stage_view_get_default_paint_flags (view) &
- CLUTTER_PAINT_FLAG_NO_CURSORS)
- continue;
-
- if (meta_stage_view_is_cursor_overlay_inhibited (META_STAGE_VIEW (view)))
- return;
+ clutter_stage_view_add_redraw_clip (view, &view_clip);
+ clutter_stage_view_schedule_update (view);
+ }
+}
- clutter_stage_view_get_layout (view, &view_layout);
+static void
+cursor_rect_to_clip (const graphene_rect_t *cursor_rect,
+ MtkRectangle *clip_rect)
+{
+ mtk_rectangle_from_graphene_rect (cursor_rect,
+ MTK_ROUNDING_STRATEGY_GROW,
+ clip_rect);
- if (mtk_rectangle_intersect (&clip, &view_layout, &view_clip))
- {
- clutter_stage_view_add_redraw_clip (view, &view_clip);
- clutter_stage_view_schedule_update (view);
- }
- }
+ /* Since we're flooring the coordinates, we need to enlarge the clip by the
+ * difference between the actual coordinate and the floored value */
+ clip_rect->width += (int) ceilf (cursor_rect->origin.x - clip_rect->x) * 2;
+ clip_rect->height += (int) ceilf (cursor_rect->origin.y - clip_rect->y) * 2;
}
static void
queue_redraw_for_cursor_overlay (MetaStage *stage,
MetaOverlay *overlay)
{
- /* Clear the location the overlay was at before, if we need to. */
- if (overlay->previous_is_valid)
- {
- queue_cursor_overlay_redraw_clutter_rect (stage,
- overlay,
- &overlay->previous_rect);
- overlay->previous_is_valid = FALSE;
- }
+ GList *l;
- /* Draw the overlay at the new position */
- if (overlay->is_visible && overlay->texture)
+ for (l = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
+ l;
+ l = l->next)
{
- queue_cursor_overlay_redraw_clutter_rect (stage,
- overlay,
- &overlay->current_rect);
+ ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data);
+ MetaOverlayViewState *view_state;
+
+ view_state = ensure_view_state (overlay, view);
+ if (view_state->has_painted_rect)
+ {
+ MtkRectangle clip;
+
+ cursor_rect_to_clip (&view_state->painted_rect, &clip);
+ intersect_and_queue_redraw (view, &clip);
+ }
+
+ if (overlay->is_visible &&
+ overlay->texture &&
+ !(clutter_stage_view_get_default_paint_flags (view) &
+ CLUTTER_PAINT_FLAG_NO_CURSORS) &&
+ !meta_stage_view_is_cursor_overlay_inhibited (META_STAGE_VIEW (view)))
+ {
+ MtkRectangle clip;
+
+ cursor_rect_to_clip (&overlay->current_rect, &clip);
+ intersect_and_queue_redraw (view, &clip);
+ }
}
}
@@ -509,4 +556,8 @@ meta_stage_rebuild_views (MetaStage *stage)
meta_monitor_manager_get_screen_size (monitor_manager, &width, &height);
clutter_actor_set_size (CLUTTER_ACTOR (stage), width, height);
+
+ g_list_foreach (stage->overlays,
+ (GFunc) meta_overlay_invalidate_views,
+ NULL);
}
--
2.44.0.501.g19981daefd.dirty