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 "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

View File

@ -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 */

View File

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

View File

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

View File

@ -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() */

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_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,
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__ */