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:
parent
ab835787bd
commit
8b3c1f4b87
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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() */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user