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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2726>
This commit is contained in:
msizanoen1 2023-04-07 22:19:34 +07:00 committed by Marge Bot
parent ab835787bd
commit 8b3c1f4b87
7 changed files with 147 additions and 177 deletions

View File

@ -23,8 +23,10 @@
#include "config.h" #include "config.h"
#include "clutter/clutter-mutter.h"
#include "compositor/clutter-utils.h" #include "compositor/clutter-utils.h"
#include "compositor/meta-cullable.h" #include "compositor/meta-cullable.h"
#include "compositor/region-utils.h"
G_DEFINE_INTERFACE (MetaCullable, meta_cullable, CLUTTER_TYPE_ACTOR); G_DEFINE_INTERFACE (MetaCullable, meta_cullable, CLUTTER_TYPE_ACTOR);
@ -44,6 +46,16 @@ has_active_effects (ClutterActor *actor)
return FALSE; 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 * SECTION:meta-cullable
* @title: MetaCullable * @title: MetaCullable
@ -86,7 +98,6 @@ meta_cullable_cull_out_children (MetaCullable *cullable,
clutter_actor_iter_init (&iter, actor); clutter_actor_iter_init (&iter, actor);
while (clutter_actor_iter_prev (&iter, &child)) while (clutter_actor_iter_prev (&iter, &child))
{ {
float x, y;
gboolean needs_culling; gboolean needs_culling;
if (!META_IS_CULLABLE (child)) if (!META_IS_CULLABLE (child))
@ -116,21 +127,60 @@ meta_cullable_cull_out_children (MetaCullable *cullable,
if (needs_culling && has_active_effects (child)) if (needs_culling && has_active_effects (child))
needs_culling = FALSE; needs_culling = FALSE;
if (needs_culling && !meta_cullable_is_untransformed (META_CULLABLE (child)))
needs_culling = FALSE;
if (needs_culling) 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 */ clutter_actor_get_transform (child, &actor_transform);
cairo_region_translate (unobscured_region, - x, - y);
cairo_region_translate (clip_region, - x, - y);
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); if (!graphene_matrix_inverse (&actor_transform,
cairo_region_translate (clip_region, x, y); &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 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 static void
meta_cullable_default_init (MetaCullableInterface *iface) 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_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: * meta_cullable_reset_culling:
* @cullable: The #MetaCullable * @cullable: The #MetaCullable

View File

@ -39,14 +39,12 @@ struct _MetaCullableInterface
void (* cull_out) (MetaCullable *cullable, void (* cull_out) (MetaCullable *cullable,
cairo_region_t *unobscured_region, cairo_region_t *unobscured_region,
cairo_region_t *clip_region); cairo_region_t *clip_region);
gboolean (* is_untransformed) (MetaCullable *cullable);
void (* reset_culling) (MetaCullable *cullable); void (* reset_culling) (MetaCullable *cullable);
}; };
void meta_cullable_cull_out (MetaCullable *cullable, void meta_cullable_cull_out (MetaCullable *cullable,
cairo_region_t *unobscured_region, cairo_region_t *unobscured_region,
cairo_region_t *clip_region); cairo_region_t *clip_region);
gboolean meta_cullable_is_untransformed (MetaCullable *cullable);
void meta_cullable_reset_culling (MetaCullable *cullable); void meta_cullable_reset_culling (MetaCullable *cullable);
/* Utility methods for implementations */ /* Utility methods for implementations */

View File

@ -77,43 +77,6 @@ effective_unobscured_region (MetaSurfaceActor *surface_actor)
return priv->unobscured_region; 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 static void
set_unobscured_region (MetaSurfaceActor *surface_actor, set_unobscured_region (MetaSurfaceActor *surface_actor,
cairo_region_t *unobscured_region) cairo_region_t *unobscured_region)
@ -141,9 +104,7 @@ set_unobscured_region (MetaSurfaceActor *surface_actor,
.height = height, .height = height,
}; };
priv->unobscured_region = get_scaled_region (surface_actor, priv->unobscured_region = cairo_region_copy (unobscured_region);
unobscured_region,
IN_ACTOR_PERSPECTIVE);
cairo_region_intersect_rectangle (priv->unobscured_region, &bounds); 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)) 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_copy = cairo_region_copy (clip_region);
clip_region, meta_shaped_texture_set_clip_region (stex, clip_region_copy);
IN_ACTOR_PERSPECTIVE);
meta_shaped_texture_set_clip_region (stex, region);
cairo_region_destroy (region); cairo_region_destroy (clip_region_copy);
} }
else else
{ {
@ -293,47 +252,19 @@ meta_surface_actor_cull_out (MetaCullable *cullable,
if (opacity == 0xff) if (opacity == 0xff)
{ {
cairo_region_t *opaque_region; cairo_region_t *opaque_region;
cairo_region_t *scaled_opaque_region;
opaque_region = meta_shaped_texture_get_opaque_region (priv->texture); opaque_region = meta_shaped_texture_get_opaque_region (priv->texture);
if (!opaque_region) if (!opaque_region)
return; return;
scaled_opaque_region = get_scaled_region (surface_actor,
opaque_region,
IN_STAGE_PERSPECTIVE);
if (unobscured_region) if (unobscured_region)
cairo_region_subtract (unobscured_region, scaled_opaque_region); cairo_region_subtract (unobscured_region, opaque_region);
if (clip_region) if (clip_region)
cairo_region_subtract (clip_region, scaled_opaque_region); cairo_region_subtract (clip_region, opaque_region);
cairo_region_destroy (scaled_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 static void
meta_surface_actor_reset_culling (MetaCullable *cullable) meta_surface_actor_reset_culling (MetaCullable *cullable)
{ {
@ -346,7 +277,6 @@ static void
cullable_iface_init (MetaCullableInterface *iface) cullable_iface_init (MetaCullableInterface *iface)
{ {
iface->cull_out = meta_surface_actor_cull_out; iface->cull_out = meta_surface_actor_cull_out;
iface->is_untransformed = meta_surface_actor_is_untransformed;
iface->reset_culling = meta_surface_actor_reset_culling; iface->reset_culling = meta_surface_actor_reset_culling;
} }

View File

@ -87,29 +87,6 @@ surface_container_cull_out (MetaCullable *cullable,
meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); 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 static void
surface_container_reset_culling (MetaCullable *cullable) surface_container_reset_culling (MetaCullable *cullable)
{ {
@ -120,7 +97,6 @@ static void
surface_container_cullable_iface_init (MetaCullableInterface *iface) surface_container_cullable_iface_init (MetaCullableInterface *iface)
{ {
iface->cull_out = surface_container_cull_out; iface->cull_out = surface_container_cull_out;
iface->is_untransformed = surface_container_is_untransformed;
iface->reset_culling = surface_container_reset_culling; iface->reset_culling = surface_container_reset_culling;
} }

View File

@ -9,6 +9,7 @@
#include "compositor/meta-cullable.h" #include "compositor/meta-cullable.h"
#include "compositor/meta-window-actor-private.h" #include "compositor/meta-window-actor-private.h"
#include "compositor/meta-window-group-private.h" #include "compositor/meta-window-group-private.h"
#include "compositor/region-utils.h"
#include "core/display-private.h" #include "core/display-private.h"
#include "core/window-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 *clip_region;
cairo_region_t *unobscured_region; cairo_region_t *unobscured_region;
cairo_rectangle_int_t visible_rect; cairo_rectangle_int_t visible_rect;
int paint_x_origin, paint_y_origin;
int screen_width, screen_height; int screen_width, screen_height;
graphene_matrix_t stage_to_actor;
redraw_clip = clutter_paint_context_get_redraw_clip (paint_context); redraw_clip = clutter_paint_context_get_redraw_clip (paint_context);
if (!redraw_clip) if (!redraw_clip)
{ goto fail;
parent_actor_class->paint (actor, paint_context);
return;
}
meta_display_get_size (window_group->display, &screen_width, &screen_height); 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. /* 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 * 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 * case and we need to compensate.
* 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.
*/ */
if (clutter_actor_is_in_clone_paint (actor)) if (clutter_actor_is_in_clone_paint (actor))
{ {
CoglFramebuffer *fb; CoglFramebuffer *fb;
ClutterStageView *view; ClutterStageView *view;
MetaTransforms trans; graphene_matrix_t eye_to_actor, actor_to_eye, stage_to_eye;
fb = clutter_paint_context_get_framebuffer (paint_context); fb = clutter_paint_context_get_framebuffer (paint_context);
view = clutter_paint_context_get_stage_view (paint_context); view = clutter_paint_context_get_stage_view (paint_context);
if (!view || if (!view ||
fb != clutter_stage_view_get_framebuffer (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)))
{ {
parent_actor_class->paint (actor, paint_context); goto fail;
return;
} }
paint_x_origin = trans.x_origin; cogl_framebuffer_get_modelview_matrix (fb, &actor_to_eye);
paint_y_origin = trans.y_origin;
/* 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 else
{ {
paint_x_origin = 0; graphene_matrix_t actor_to_stage;
paint_y_origin = 0;
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.x = visible_rect.y = 0;
visible_rect.width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); visible_rect.width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
visible_rect.height = clutter_actor_get_height (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 * multihead setup with mismatched monitor sizes, we could intersect this
* with an accurate union of the monitors to avoid painting shadows that are * with an accurate union of the monitors to avoid painting shadows that are
* visible only in the holes. */ * visible only in the holes. */
clip_region = cairo_region_copy (redraw_clip); clip_region = meta_region_apply_matrix_transform_expand (redraw_clip,
&stage_to_actor);
cairo_region_translate (clip_region, -paint_x_origin, -paint_y_origin);
meta_cullable_cull_out (META_CULLABLE (window_group), unobscured_region, clip_region); 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); parent_actor_class->paint (actor, paint_context);
meta_cullable_reset_culling (META_CULLABLE (window_group)); 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() */ /* Adapted from clutter_actor_update_default_paint_volume() */

View File

@ -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_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;
}

View File

@ -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, void meta_region_to_cairo_path (cairo_region_t *region,
cairo_t *cr); 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__ */ #endif /* __META_REGION_UTILS_H__ */