
Transfer none was achieved using a stack GArray in the stage which would get resized to 0 at the end of every frame to "free" it. In the case of direct scanout however, painting the next frame only happens after leaving fullscreen again. Until then the array just kept growing and because GArrays don't reallocate when shrunk, this memory remained allocated even after leaving fullscreen. There is no cache benefit from storing paint volumes this way, because nothing accesses them after their immediate use in the calling code. Also the reduced overhead from avoiding malloc calls seems negligible as according to heaptrack this only makes up about 2-3% of the temporary allocations. Changing this to transfer full and removing the stack array simplifies the code and fixes the "leak". Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3191 Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3442>
221 lines
6.9 KiB
C
221 lines
6.9 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
#include "config.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "compositor/clutter-utils.h"
|
|
#include "compositor/compositor-private.h"
|
|
#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"
|
|
|
|
struct _MetaWindowGroupClass
|
|
{
|
|
ClutterActorClass parent_class;
|
|
};
|
|
|
|
struct _MetaWindowGroup
|
|
{
|
|
ClutterActor parent;
|
|
|
|
MetaDisplay *display;
|
|
};
|
|
|
|
static void cullable_iface_init (MetaCullableInterface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaWindowGroup, meta_window_group, CLUTTER_TYPE_ACTOR,
|
|
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
|
|
|
|
static void
|
|
meta_window_group_cull_unobscured (MetaCullable *cullable,
|
|
MtkRegion *unobscured_region)
|
|
{
|
|
meta_cullable_cull_unobscured_children (cullable, unobscured_region);
|
|
}
|
|
|
|
static void
|
|
meta_window_group_cull_redraw_clip (MetaCullable *cullable,
|
|
MtkRegion *clip_region)
|
|
{
|
|
meta_cullable_cull_redraw_clip_children (cullable, clip_region);
|
|
}
|
|
|
|
static void
|
|
cullable_iface_init (MetaCullableInterface *iface)
|
|
{
|
|
iface->cull_unobscured = meta_window_group_cull_unobscured;
|
|
iface->cull_redraw_clip = meta_window_group_cull_redraw_clip;
|
|
}
|
|
|
|
static void
|
|
meta_window_group_paint (ClutterActor *actor,
|
|
ClutterPaintContext *paint_context)
|
|
{
|
|
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
|
|
ClutterActorClass *parent_actor_class =
|
|
CLUTTER_ACTOR_CLASS (meta_window_group_parent_class);
|
|
ClutterActor *stage = clutter_actor_get_stage (actor);
|
|
const MtkRegion *redraw_clip;
|
|
g_autoptr (MtkRegion) clip_region = NULL;
|
|
graphene_matrix_t stage_to_actor;
|
|
|
|
redraw_clip = clutter_paint_context_get_redraw_clip (paint_context);
|
|
if (!redraw_clip)
|
|
goto fail;
|
|
|
|
/* 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.
|
|
*/
|
|
if (clutter_actor_is_in_clone_paint (actor))
|
|
{
|
|
CoglFramebuffer *fb;
|
|
ClutterStageView *view;
|
|
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))
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
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
|
|
{
|
|
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;
|
|
|
|
/* Get the clipped redraw bounds so that we can avoid painting shadows on
|
|
* windows that don't need to be painted in this frame. In the case of a
|
|
* 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 = meta_region_apply_matrix_transform_expand (redraw_clip,
|
|
&stage_to_actor);
|
|
|
|
meta_cullable_cull_redraw_clip (META_CULLABLE (window_group), clip_region);
|
|
|
|
parent_actor_class->paint (actor, paint_context);
|
|
|
|
meta_cullable_cull_redraw_clip (META_CULLABLE (window_group), NULL);
|
|
|
|
return;
|
|
|
|
fail:
|
|
parent_actor_class->paint (actor, paint_context);
|
|
}
|
|
|
|
/* Adapted from clutter_actor_update_default_paint_volume() */
|
|
static gboolean
|
|
meta_window_group_get_paint_volume (ClutterActor *self,
|
|
ClutterPaintVolume *volume)
|
|
{
|
|
ClutterActorIter iter;
|
|
ClutterActor *child;
|
|
|
|
clutter_actor_iter_init (&iter, self);
|
|
while (clutter_actor_iter_next (&iter, &child))
|
|
{
|
|
g_autoptr (ClutterPaintVolume) child_volume = NULL;
|
|
|
|
if (!clutter_actor_is_mapped (child))
|
|
continue;
|
|
|
|
child_volume = clutter_actor_get_transformed_paint_volume (child, self);
|
|
if (child_volume == NULL)
|
|
return FALSE;
|
|
|
|
clutter_paint_volume_union (volume, child_volume);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* This is a workaround for Clutter's awful allocation tracking.
|
|
* Without this, any time the window group changed size, which is
|
|
* any time windows are dragged around, we'll do a full repaint
|
|
* of the window group, which includes the background actor, meaning
|
|
* a full-stage repaint.
|
|
*
|
|
* Since actors are allowed to paint outside their allocation, and
|
|
* since child actors are allowed to be outside their parents, this
|
|
* doesn't affect anything, but it means that we'll get much more
|
|
* sane and consistent clipped repaints from Clutter. */
|
|
static void
|
|
meta_window_group_get_preferred_width (ClutterActor *actor,
|
|
gfloat for_height,
|
|
gfloat *min_width,
|
|
gfloat *nat_width)
|
|
{
|
|
*min_width = 0;
|
|
*nat_width = 0;
|
|
}
|
|
|
|
static void
|
|
meta_window_group_get_preferred_height (ClutterActor *actor,
|
|
gfloat for_width,
|
|
gfloat *min_height,
|
|
gfloat *nat_height)
|
|
{
|
|
*min_height = 0;
|
|
*nat_height = 0;
|
|
}
|
|
|
|
static void
|
|
meta_window_group_class_init (MetaWindowGroupClass *klass)
|
|
{
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
|
|
actor_class->paint = meta_window_group_paint;
|
|
actor_class->get_paint_volume = meta_window_group_get_paint_volume;
|
|
actor_class->get_preferred_width = meta_window_group_get_preferred_width;
|
|
actor_class->get_preferred_height = meta_window_group_get_preferred_height;
|
|
}
|
|
|
|
static void
|
|
meta_window_group_init (MetaWindowGroup *window_group)
|
|
{
|
|
}
|
|
|
|
ClutterActor *
|
|
meta_window_group_new (MetaDisplay *display)
|
|
{
|
|
MetaWindowGroup *window_group;
|
|
|
|
window_group = g_object_new (META_TYPE_WINDOW_GROUP, NULL);
|
|
|
|
window_group->display = display;
|
|
|
|
return CLUTTER_ACTOR (window_group);
|
|
}
|