From 8b3c1f4b876e254d64bea6607ab668653aa705a0 Mon Sep 17 00:00:00 2001 From: msizanoen1 Date: Fri, 7 Apr 2023 22:19:34 +0700 Subject: [PATCH] cullable: Generalize actor coordinates space translation for regions This allows MetaCullable to work with actors using arbitrary transforms which will be needed for implementing surface pixel alignment for fractional-scale-v1. This also deletes meta_cullable_is_untransformed as it's no longer necessary, and we can also stop manually scaling the region objects while performing opaque region culling in surfaces since it's now handled transparently by the new `meta_cullable_cull_out_children` implementation. Part-of: --- src/compositor/meta-cullable.c | 99 +++++++++++++--------- src/compositor/meta-cullable.h | 8 +- src/compositor/meta-surface-actor.c | 84 ++---------------- src/compositor/meta-window-actor-wayland.c | 24 ------ src/compositor/meta-window-group.c | 72 ++++++++-------- src/compositor/region-utils.c | 33 ++++++++ src/compositor/region-utils.h | 4 + 7 files changed, 147 insertions(+), 177 deletions(-) diff --git a/src/compositor/meta-cullable.c b/src/compositor/meta-cullable.c index e306c56e2..8b60e36de 100644 --- a/src/compositor/meta-cullable.c +++ b/src/compositor/meta-cullable.c @@ -23,8 +23,10 @@ #include "config.h" +#include "clutter/clutter-mutter.h" #include "compositor/clutter-utils.h" #include "compositor/meta-cullable.h" +#include "compositor/region-utils.h" G_DEFINE_INTERFACE (MetaCullable, meta_cullable, CLUTTER_TYPE_ACTOR); @@ -44,6 +46,16 @@ has_active_effects (ClutterActor *actor) return FALSE; } +static cairo_region_t * +region_apply_transform_expand_maybe_ref (cairo_region_t *region, + graphene_matrix_t *transform) +{ + if (cairo_region_is_empty (region)) + return cairo_region_reference (region); + + return meta_region_apply_matrix_transform_expand (region, transform); +} + /** * SECTION:meta-cullable * @title: MetaCullable @@ -86,7 +98,6 @@ meta_cullable_cull_out_children (MetaCullable *cullable, clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_prev (&iter, &child)) { - float x, y; gboolean needs_culling; if (!META_IS_CULLABLE (child)) @@ -116,21 +127,60 @@ meta_cullable_cull_out_children (MetaCullable *cullable, if (needs_culling && has_active_effects (child)) needs_culling = FALSE; - if (needs_culling && !meta_cullable_is_untransformed (META_CULLABLE (child))) - needs_culling = FALSE; - if (needs_culling) { - clutter_actor_get_position (child, &x, &y); + cairo_region_t *actor_unobscured_region, *actor_clip_region; + cairo_region_t *reduced_unobscured_region, *reduced_clip_region; + graphene_matrix_t actor_transform, inverted_actor_transform; - /* Temporarily move to the coordinate system of the actor */ - cairo_region_translate (unobscured_region, - x, - y); - cairo_region_translate (clip_region, - x, - y); + clutter_actor_get_transform (child, &actor_transform); - meta_cullable_cull_out (META_CULLABLE (child), unobscured_region, clip_region); + if (graphene_matrix_is_identity (&actor_transform)) + { + /* No transformation needed, simply pass through to child */ + meta_cullable_cull_out (META_CULLABLE (child), + unobscured_region, + clip_region); + continue; + } - cairo_region_translate (unobscured_region, x, y); - cairo_region_translate (clip_region, x, y); + if (!graphene_matrix_inverse (&actor_transform, + &inverted_actor_transform) || + !graphene_matrix_is_2d (&actor_transform)) + { + meta_cullable_cull_out (META_CULLABLE (child), NULL, NULL); + continue; + } + + actor_unobscured_region = + region_apply_transform_expand_maybe_ref (unobscured_region, + &inverted_actor_transform); + actor_clip_region = + region_apply_transform_expand_maybe_ref (clip_region, + &inverted_actor_transform); + + g_assert (actor_unobscured_region && actor_clip_region); + + meta_cullable_cull_out (META_CULLABLE (child), + actor_unobscured_region, + actor_clip_region); + + reduced_unobscured_region = + region_apply_transform_expand_maybe_ref (actor_unobscured_region, + &actor_transform); + reduced_clip_region = + region_apply_transform_expand_maybe_ref (actor_clip_region, + &actor_transform); + + g_assert (reduced_unobscured_region && reduced_clip_region); + + cairo_region_intersect (unobscured_region, reduced_unobscured_region); + cairo_region_intersect (clip_region, reduced_clip_region); + + cairo_region_destroy (actor_unobscured_region); + cairo_region_destroy (actor_clip_region); + cairo_region_destroy (reduced_unobscured_region); + cairo_region_destroy (reduced_clip_region); } else { @@ -165,23 +215,9 @@ meta_cullable_reset_culling_children (MetaCullable *cullable) } } -static gboolean -meta_cullable_default_is_untransformed (MetaCullable *cullable) -{ - float width, height; - graphene_point3d_t verts[4]; - - clutter_actor_get_size (CLUTTER_ACTOR (cullable), &width, &height); - clutter_actor_get_abs_allocation_vertices (CLUTTER_ACTOR (cullable), verts); - - return meta_actor_vertices_are_untransformed (verts, width, height, - NULL); -} - static void meta_cullable_default_init (MetaCullableInterface *iface) { - iface->is_untransformed = meta_cullable_default_is_untransformed; } /** @@ -216,19 +252,6 @@ meta_cullable_cull_out (MetaCullable *cullable, META_CULLABLE_GET_IFACE (cullable)->cull_out (cullable, unobscured_region, clip_region); } -/** - * meta_cullable_is_untransformed: - * @cullable: The #MetaCullable - * - * Check if a cullable is "untransformed" - which actually means transformed by - * at most a integer-translation. - */ -gboolean -meta_cullable_is_untransformed (MetaCullable *cullable) -{ - return META_CULLABLE_GET_IFACE (cullable)->is_untransformed (cullable); -} - /** * meta_cullable_reset_culling: * @cullable: The #MetaCullable diff --git a/src/compositor/meta-cullable.h b/src/compositor/meta-cullable.h index 471681da3..4087bc44e 100644 --- a/src/compositor/meta-cullable.h +++ b/src/compositor/meta-cullable.h @@ -36,17 +36,15 @@ struct _MetaCullableInterface { GTypeInterface g_iface; - void (* cull_out) (MetaCullable *cullable, - cairo_region_t *unobscured_region, - cairo_region_t *clip_region); - gboolean (* is_untransformed) (MetaCullable *cullable); + void (* cull_out) (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region); void (* reset_culling) (MetaCullable *cullable); }; void meta_cullable_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region); -gboolean meta_cullable_is_untransformed (MetaCullable *cullable); void meta_cullable_reset_culling (MetaCullable *cullable); /* Utility methods for implementations */ diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c index 1bc58667a..e140261f4 100644 --- a/src/compositor/meta-surface-actor.c +++ b/src/compositor/meta-surface-actor.c @@ -77,43 +77,6 @@ effective_unobscured_region (MetaSurfaceActor *surface_actor) return priv->unobscured_region; } -static cairo_region_t* -get_scaled_region (MetaSurfaceActor *surface_actor, - cairo_region_t *region, - ScalePerspectiveType scale_perspective) -{ - MetaWindowActor *window_actor; - cairo_region_t *scaled_region = NULL; - int geometry_scale; - float x, y; - - window_actor = meta_window_actor_from_actor (CLUTTER_ACTOR (surface_actor)); - geometry_scale = meta_window_actor_get_geometry_scale (window_actor); - - clutter_actor_get_position (CLUTTER_ACTOR (surface_actor), &x, &y); - cairo_region_translate (region, x, y); - - switch (scale_perspective) - { - case IN_STAGE_PERSPECTIVE: - scaled_region = meta_region_scale_double (region, - geometry_scale, - META_ROUNDING_STRATEGY_GROW); - break; - case IN_ACTOR_PERSPECTIVE: - scaled_region = meta_region_scale_double (region, - 1.0 / geometry_scale, - META_ROUNDING_STRATEGY_GROW); - break; - } - - g_assert (scaled_region != NULL); - cairo_region_translate (region, -x, -y); - cairo_region_translate (scaled_region, -x, -y); - - return scaled_region; -} - static void set_unobscured_region (MetaSurfaceActor *surface_actor, cairo_region_t *unobscured_region) @@ -141,9 +104,7 @@ set_unobscured_region (MetaSurfaceActor *surface_actor, .height = height, }; - priv->unobscured_region = get_scaled_region (surface_actor, - unobscured_region, - IN_ACTOR_PERSPECTIVE); + priv->unobscured_region = cairo_region_copy (unobscured_region); cairo_region_intersect_rectangle (priv->unobscured_region, &bounds); } @@ -160,14 +121,12 @@ set_clip_region (MetaSurfaceActor *surface_actor, if (clip_region && !cairo_region_is_empty (clip_region)) { - cairo_region_t *region; + cairo_region_t *clip_region_copy; - region = get_scaled_region (surface_actor, - clip_region, - IN_ACTOR_PERSPECTIVE); - meta_shaped_texture_set_clip_region (stex, region); + clip_region_copy = cairo_region_copy (clip_region); + meta_shaped_texture_set_clip_region (stex, clip_region_copy); - cairo_region_destroy (region); + cairo_region_destroy (clip_region_copy); } else { @@ -293,47 +252,19 @@ meta_surface_actor_cull_out (MetaCullable *cullable, if (opacity == 0xff) { cairo_region_t *opaque_region; - cairo_region_t *scaled_opaque_region; opaque_region = meta_shaped_texture_get_opaque_region (priv->texture); if (!opaque_region) return; - scaled_opaque_region = get_scaled_region (surface_actor, - opaque_region, - IN_STAGE_PERSPECTIVE); - if (unobscured_region) - cairo_region_subtract (unobscured_region, scaled_opaque_region); + cairo_region_subtract (unobscured_region, opaque_region); if (clip_region) - cairo_region_subtract (clip_region, scaled_opaque_region); - - cairo_region_destroy (scaled_opaque_region); + cairo_region_subtract (clip_region, opaque_region); } } -static gboolean -meta_surface_actor_is_untransformed (MetaCullable *cullable) -{ - ClutterActor *actor = CLUTTER_ACTOR (cullable); - MetaWindowActor *window_actor; - float width, height; - graphene_point3d_t verts[4]; - int geometry_scale; - - clutter_actor_get_size (actor, &width, &height); - clutter_actor_get_abs_allocation_vertices (actor, verts); - - window_actor = meta_window_actor_from_actor (actor); - geometry_scale = meta_window_actor_get_geometry_scale (window_actor); - - return meta_actor_vertices_are_untransformed (verts, - width * geometry_scale, - height * geometry_scale, - NULL); -} - static void meta_surface_actor_reset_culling (MetaCullable *cullable) { @@ -346,7 +277,6 @@ static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_surface_actor_cull_out; - iface->is_untransformed = meta_surface_actor_is_untransformed; iface->reset_culling = meta_surface_actor_reset_culling; } diff --git a/src/compositor/meta-window-actor-wayland.c b/src/compositor/meta-window-actor-wayland.c index a74a19aba..b465a793b 100644 --- a/src/compositor/meta-window-actor-wayland.c +++ b/src/compositor/meta-window-actor-wayland.c @@ -87,29 +87,6 @@ surface_container_cull_out (MetaCullable *cullable, meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); } -static gboolean -surface_container_is_untransformed (MetaCullable *cullable) -{ - MetaSurfaceContainerActorWayland *surface_container = - META_SURFACE_CONTAINER_ACTOR_WAYLAND (cullable); - ClutterActor *actor = CLUTTER_ACTOR (cullable); - MetaWindowActor *window_actor; - float width, height; - graphene_point3d_t verts[4]; - int geometry_scale; - - clutter_actor_get_size (actor, &width, &height); - clutter_actor_get_abs_allocation_vertices (actor, verts); - - window_actor = surface_container->window_actor; - geometry_scale = meta_window_actor_get_geometry_scale (window_actor); - - return meta_actor_vertices_are_untransformed (verts, - width * geometry_scale, - height * geometry_scale, - NULL); -} - static void surface_container_reset_culling (MetaCullable *cullable) { @@ -120,7 +97,6 @@ static void surface_container_cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = surface_container_cull_out; - iface->is_untransformed = surface_container_is_untransformed; iface->reset_culling = surface_container_reset_culling; } diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c index f5d8b9a62..2889bbc10 100644 --- a/src/compositor/meta-window-group.c +++ b/src/compositor/meta-window-group.c @@ -9,6 +9,7 @@ #include "compositor/meta-cullable.h" #include "compositor/meta-window-actor-private.h" #include "compositor/meta-window-group-private.h" +#include "compositor/region-utils.h" #include "core/display-private.h" #include "core/window-private.h" @@ -62,62 +63,63 @@ meta_window_group_paint (ClutterActor *actor, cairo_region_t *clip_region; cairo_region_t *unobscured_region; cairo_rectangle_int_t visible_rect; - int paint_x_origin, paint_y_origin; int screen_width, screen_height; + graphene_matrix_t stage_to_actor; redraw_clip = clutter_paint_context_get_redraw_clip (paint_context); if (!redraw_clip) - { - parent_actor_class->paint (actor, paint_context); - return; - } + goto fail; meta_display_get_size (window_group->display, &screen_width, &screen_height); /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the - * case and we need to compensate. We look at the position of the window - * group under the current model-view matrix and the position of the actor. - * If they are both simply integer translations, then we can compensate - * easily, otherwise we give up. - * - * Possible cleanup: work entirely in paint space - we can compute the - * combination of the model-view matrix with the local matrix for each child - * actor and get a total transformation for that actor for how we are - * painting currently, and never worry about how actors are positioned - * on the stage. + * case and we need to compensate. */ if (clutter_actor_is_in_clone_paint (actor)) { CoglFramebuffer *fb; ClutterStageView *view; - MetaTransforms trans; + graphene_matrix_t eye_to_actor, actor_to_eye, stage_to_eye; fb = clutter_paint_context_get_framebuffer (paint_context); view = clutter_paint_context_get_stage_view (paint_context); + if (!view || - fb != clutter_stage_view_get_framebuffer (view) || - !meta_actor_painting_untransformed (fb, - screen_width, - screen_height, - screen_width, - screen_height, - &trans) || - !meta_cullable_is_untransformed (META_CULLABLE (actor))) + fb != clutter_stage_view_get_framebuffer (view)) { - parent_actor_class->paint (actor, paint_context); - return; + goto fail; } - paint_x_origin = trans.x_origin; - paint_y_origin = trans.y_origin; + cogl_framebuffer_get_modelview_matrix (fb, &actor_to_eye); + + /* We need to obtain the transformation matrix from eye coordinates + * to cloned actor coordinates to be able to deduce the transformation + * matrix from stage to cloned actor coordinates, which is needed to + * calculate the redraw clip for the current actor. + * If we cannot do this because the cloned actor modelview matrix is + * non-invertible, give up on culling. + */ + if (!graphene_matrix_inverse (&actor_to_eye, &eye_to_actor)) + goto fail; + + clutter_actor_get_transform (stage, &stage_to_eye); + graphene_matrix_multiply (&stage_to_eye, &eye_to_actor, + &stage_to_actor); } else { - paint_x_origin = 0; - paint_y_origin = 0; + graphene_matrix_t actor_to_stage; + + clutter_actor_get_relative_transformation_matrix (actor, stage, + &actor_to_stage); + if (!graphene_matrix_inverse (&actor_to_stage, &stage_to_actor)) + goto fail; } + if (!graphene_matrix_is_2d (&stage_to_actor)) + goto fail; + visible_rect.x = visible_rect.y = 0; visible_rect.width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); visible_rect.height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); @@ -129,9 +131,8 @@ meta_window_group_paint (ClutterActor *actor, * multihead setup with mismatched monitor sizes, we could intersect this * with an accurate union of the monitors to avoid painting shadows that are * visible only in the holes. */ - clip_region = cairo_region_copy (redraw_clip); - - cairo_region_translate (clip_region, -paint_x_origin, -paint_y_origin); + clip_region = meta_region_apply_matrix_transform_expand (redraw_clip, + &stage_to_actor); meta_cullable_cull_out (META_CULLABLE (window_group), unobscured_region, clip_region); @@ -141,6 +142,11 @@ meta_window_group_paint (ClutterActor *actor, parent_actor_class->paint (actor, paint_context); meta_cullable_reset_culling (META_CULLABLE (window_group)); + + return; + +fail: + parent_actor_class->paint (actor, paint_context); } /* Adapted from clutter_actor_update_default_paint_volume() */ diff --git a/src/compositor/region-utils.c b/src/compositor/region-utils.c index e00f123d3..892d59fc1 100644 --- a/src/compositor/region-utils.c +++ b/src/compositor/region-utils.c @@ -472,3 +472,36 @@ meta_region_to_cairo_path (cairo_region_t *region, cairo_rectangle (cr, rect.x, rect.y, rect.width, rect.height); } } + +cairo_region_t * +meta_region_apply_matrix_transform_expand (const cairo_region_t *region, + graphene_matrix_t *transform) +{ + int n_rects, i; + cairo_rectangle_int_t *rects; + cairo_region_t *transformed_region; + + if (graphene_matrix_is_identity (transform)) + return cairo_region_copy (region); + + n_rects = cairo_region_num_rectangles (region); + META_REGION_CREATE_RECTANGLE_ARRAY_SCOPED (n_rects, rects); + for (i = 0; i < n_rects; i++) + { + graphene_rect_t transformed_rect, rect; + cairo_rectangle_int_t int_rect; + + cairo_region_get_rectangle (region, i, &int_rect); + rect = meta_rectangle_to_graphene_rect (&int_rect); + + graphene_matrix_transform_bounds (transform, &rect, &transformed_rect); + + meta_rectangle_from_graphene_rect (&transformed_rect, + META_ROUNDING_STRATEGY_GROW, + &rects[i]); + } + + transformed_region = cairo_region_create_rectangles (rects, n_rects); + + return transformed_region; +} diff --git a/src/compositor/region-utils.h b/src/compositor/region-utils.h index a3b89f994..cf7315192 100644 --- a/src/compositor/region-utils.h +++ b/src/compositor/region-utils.h @@ -119,4 +119,8 @@ cairo_region_t * meta_region_crop_and_scale (cairo_region_t *region, void meta_region_to_cairo_path (cairo_region_t *region, cairo_t *cr); +cairo_region_t * +meta_region_apply_matrix_transform_expand (const cairo_region_t *region, + graphene_matrix_t *transform); + #endif /* __META_REGION_UTILS_H__ */