1
0
Fork 0

clutter: Add API to get the resource scale of an actor

A clutter actor might be painted on a stage view with a view scale
other than 1. In this case, to show the content in full resolution, the
actor must use a higher resolution resource (e.g. texture), which will
be down scaled to the stage coordinate space, then scaled up again to
the stage view framebuffer scale.

Use a 'resource-scale' property to save information and notify when it
changes.

The resource scale is the ceiled value of the highest stage view scale a
actor is visible on. The value is ceiled because using a higher
resolution resource consistently results in better output quality. One
reason for this is that rendering is often not perfectly pixel aligned,
meaning even if we load a resource with a suitable size, due to us still
scaling ever so slightly, the quality is affected. Using a higher
resolution resource avoids this problem.

For situations inside clutter where the actual maximum view scale is
needed, a function _clutter_actor_get_real_resource_scale() is provided,
which returns the non-ceiled value.

Make sure we ignore resource scale computation requests during size
requests or allocation while ensure we've proper resource-scale on
pre-paint.

https://bugzilla.gnome.org/show_bug.cgi?id=765011
https://gitlab.gnome.org/GNOME/mutter/merge_requests/3
This commit is contained in:
Jonas Ådahl 2017-04-07 14:06:36 +02:00 committed by Marco Trevisan
parent 789a3ef029
commit ad5555bf42
9 changed files with 287 additions and 9 deletions

View file

@ -315,8 +315,11 @@ void _clutter_actor_detach_clone
void _clutter_actor_queue_redraw_on_clones (ClutterActor *actor);
void _clutter_actor_queue_relayout_on_clones (ClutterActor *actor);
void _clutter_actor_queue_only_relayout (ClutterActor *actor);
void _clutter_actor_queue_update_resource_scale_recursive (ClutterActor *actor);
CoglFramebuffer * _clutter_actor_get_active_framebuffer (ClutterActor *actor);
gboolean _clutter_actor_get_real_resource_scale (ClutterActor *actor,
float *resource_scale);
ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self,
CoglTexture *texture);

View file

@ -699,6 +699,8 @@ struct _ClutterActorPrivate
/* the cached transformation matrix; see apply_transform() */
CoglMatrix transform;
float resource_scale;
guint8 opacity;
gint opacity_override;
@ -844,6 +846,7 @@ struct _ClutterActorPrivate
guint needs_y_expand : 1;
guint needs_paint_volume_update : 1;
guint had_effects_on_last_paint_volume_update : 1;
guint needs_compute_resource_scale : 1;
};
enum
@ -916,6 +919,7 @@ enum
PROP_SCALE_CENTER_X, /* XXX:2.0 remove */
PROP_SCALE_CENTER_Y, /* XXX:2.0 remove */
PROP_SCALE_GRAVITY, /* XXX:2.0 remove */
PROP_RESOURCE_SCALE,
PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */
PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */
@ -1095,6 +1099,8 @@ static void clutter_actor_set_child_transform_internal (ClutterActor *sel
static void clutter_actor_realize_internal (ClutterActor *self);
static void clutter_actor_unrealize_internal (ClutterActor *self);
static gboolean clutter_actor_update_resource_scale (ClutterActor *self);
static void clutter_actor_ensure_resource_scale (ClutterActor *self);
static void clutter_actor_push_in_cloned_branch (ClutterActor *self,
gulong count);
@ -1521,6 +1527,8 @@ clutter_actor_real_map (ClutterActor *self)
priv->pick_id,
_clutter_actor_get_debug_name (self));
clutter_actor_ensure_resource_scale (self);
/* notify on parent mapped before potentially mapping
* children, so apps see a top-down notification.
*/
@ -2778,6 +2786,16 @@ clutter_actor_real_queue_redraw (ClutterActor *self,
return FALSE;
}
static inline gboolean
clutter_actor_needs_relayout (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
return (priv->needs_width_request ||
priv->needs_height_request ||
priv->needs_allocation);
}
static void
clutter_actor_real_queue_relayout (ClutterActor *self)
{
@ -3837,6 +3855,8 @@ clutter_actor_paint (ClutterActor *self)
if (!CLUTTER_ACTOR_IS_MAPPED (self))
return;
clutter_actor_ensure_resource_scale (self);
stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
/* mark that we are in the paint process */
@ -4339,7 +4359,10 @@ clutter_actor_remove_child_internal (ClutterActor *self,
/* clutter_actor_reparent() will emit ::parent-set for us */
if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
{
child->priv->needs_compute_resource_scale = TRUE;
g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
}
/* if the child was mapped then we need to relayout ourselves to account
* for the removed child
@ -5664,6 +5687,16 @@ clutter_actor_get_property (GObject *object,
g_value_set_enum (value, clutter_actor_get_scale_gravity (actor));
break;
case PROP_RESOURCE_SCALE:
if (priv->needs_compute_resource_scale)
{
if (!clutter_actor_update_resource_scale (actor))
g_warning ("Getting invalid resource scale property");
}
g_value_set_float (value, priv->resource_scale);
break;
case PROP_REACTIVE:
g_value_set_boolean (value, clutter_actor_get_reactive (actor));
break;
@ -7117,6 +7150,19 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_PARAM_STATIC_STRINGS |
G_PARAM_DEPRECATED);
/**
* ClutterActor:resource-scale:
*
* The resource-scale of the #ClutterActor if any or -1 if not available
*/
obj_props[PROP_RESOURCE_SCALE] =
g_param_spec_float ("resource-scale",
P_("Resource Scale"),
P_("The Scaling factor for resources painting"),
-1.0f, G_MAXFLOAT,
1.0f,
CLUTTER_PARAM_READABLE);
/**
* ClutterActor:rotation-angle-x:
*
@ -8556,11 +8602,13 @@ clutter_actor_init (ClutterActor *self)
priv->opacity = 0xff;
priv->show_on_set_parent = TRUE;
priv->resource_scale = -1.0f;
priv->needs_width_request = TRUE;
priv->needs_height_request = TRUE;
priv->needs_allocation = TRUE;
priv->needs_paint_volume_update = TRUE;
priv->needs_compute_resource_scale = TRUE;
priv->cached_width_age = 1;
priv->cached_height_age = 1;
@ -9520,6 +9568,8 @@ clutter_actor_get_preferred_width (ClutterActor *self,
return;
}
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH);
/* the remaining cases are:
*
* - either min_width or natural_width have been set
@ -9611,6 +9661,8 @@ clutter_actor_get_preferred_width (ClutterActor *self,
if (natural_width_p)
*natural_width_p = request_natural_width;
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH);
}
/**
@ -9664,6 +9716,8 @@ clutter_actor_get_preferred_height (ClutterActor *self,
return;
}
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT);
/* the remaining cases are:
*
* - either min_height or natural_height have been set
@ -9754,6 +9808,8 @@ clutter_actor_get_preferred_height (ClutterActor *self,
if (natural_height_p)
*natural_height_p = request_natural_height;
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT);
}
/**
@ -10096,6 +10152,9 @@ clutter_actor_allocate (ClutterActor *self,
if (CLUTTER_ACTOR_IS_MAPPED (self))
self->priv->needs_paint_volume_update = TRUE;
if (stage_allocation_changed)
priv->needs_compute_resource_scale = TRUE;
if (!stage_allocation_changed)
{
/* If the actor didn't move but needs_allocation is set, we just
@ -12944,7 +13003,10 @@ clutter_actor_add_child_internal (ClutterActor *self,
/* clutter_actor_reparent() will emit ::parent-set for us */
if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
{
child->priv->needs_compute_resource_scale = TRUE;
g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
}
if (check_state)
{
@ -12977,9 +13039,7 @@ clutter_actor_add_child_internal (ClutterActor *self,
/* maintain the invariant that if an actor needs layout,
* its parents do as well
*/
if (child->priv->needs_width_request ||
child->priv->needs_height_request ||
child->priv->needs_allocation)
if (clutter_actor_needs_relayout (child))
{
/* we work around the short-circuiting we do
* in clutter_actor_queue_relayout() since we
@ -13548,6 +13608,8 @@ clutter_actor_reparent (ClutterActor *self,
insert_child_at_depth,
NULL);
priv->needs_compute_resource_scale = TRUE;
/* we emit the ::parent-set signal once */
g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent);
@ -17717,6 +17779,185 @@ clutter_actor_get_paint_box (ClutterActor *self,
return TRUE;
}
static gboolean
_clutter_actor_get_resource_scale_for_rect (ClutterActor *self,
ClutterRect *bounding_rect,
float *resource_scale)
{
ClutterActor *stage;
GList *views;
GList *l;
float max_scale = 0;
stage = _clutter_actor_get_stage_internal (self);
if (!stage)
return FALSE;
views = _clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
for (l = views; l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
ClutterRect view_rect;
clutter_stage_view_get_layout (view, &view_layout);
_clutter_util_rect_from_rectangle (&view_layout, &view_rect);
if (clutter_rect_intersection (&view_rect, bounding_rect, NULL))
max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
}
if (max_scale == 0)
return FALSE;
*resource_scale = max_scale;
return TRUE;
}
static gboolean
_clutter_actor_compute_resource_scale (ClutterActor *self,
float *resource_scale)
{
ClutterRect bounding_rect;
ClutterActorPrivate *priv = self->priv;
if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
CLUTTER_ACTOR_IN_PREF_SIZE (self) ||
!clutter_actor_is_mapped (self))
{
return FALSE;
}
clutter_actor_get_transformed_position (self,
&bounding_rect.origin.x,
&bounding_rect.origin.y);
clutter_actor_get_transformed_size (self,
&bounding_rect.size.width,
&bounding_rect.size.height);
if (bounding_rect.size.width == 0.0 ||
bounding_rect.size.height == 0.0 ||
!_clutter_actor_get_resource_scale_for_rect (self,
&bounding_rect,
resource_scale))
{
if (priv->parent)
return _clutter_actor_compute_resource_scale (priv->parent,
resource_scale);
else
return FALSE;
}
return TRUE;
}
static ClutterActorTraverseVisitFlags
queue_update_resource_scale_cb (ClutterActor *actor,
int depth,
void *user_data)
{
actor->priv->needs_compute_resource_scale = TRUE;
return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
}
void
_clutter_actor_queue_update_resource_scale_recursive (ClutterActor *self)
{
_clutter_actor_traverse (self,
CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
queue_update_resource_scale_cb,
NULL,
NULL);
}
static gboolean
clutter_actor_update_resource_scale (ClutterActor *self)
{
ClutterActorPrivate *priv;
float resource_scale;
float old_resource_scale;
priv = self->priv;
g_return_val_if_fail (priv->needs_compute_resource_scale, FALSE);
old_resource_scale = priv->resource_scale;
priv->resource_scale = -1.0f;
if (_clutter_actor_compute_resource_scale (self, &resource_scale))
{
priv->resource_scale = resource_scale;
priv->needs_compute_resource_scale = FALSE;
return fabsf (old_resource_scale - resource_scale) > FLT_EPSILON;
}
return FALSE;
}
static void
clutter_actor_ensure_resource_scale (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
if (!priv->needs_compute_resource_scale)
return;
if (clutter_actor_update_resource_scale (self))
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_RESOURCE_SCALE]);
}
gboolean
_clutter_actor_get_real_resource_scale (ClutterActor *self,
gfloat *resource_scale)
{
ClutterActorPrivate *priv = self->priv;
clutter_actor_ensure_resource_scale (self);
if (!priv->needs_compute_resource_scale)
{
*resource_scale = priv->resource_scale;
return TRUE;
}
*resource_scale = -1.0f;
return FALSE;
}
/**
* clutter_actor_get_resource_scale:
* @self: A #ClutterActor
* @resource_scale: (out): return location for the resource scale
*
* Retrieves the resource scale for this actor, if available.
*
* The resource scale refers to the scale the actor should use for its resources.
* For example if an actor draws a a picture of size 100 x 100 in the stage
* coordinate space, it should use a texture of twice the size (i.e. 200 x 200)
* if the resource scale is 2.
*
* The resource scale is determined by calculating the highest #ClutterStageView
* scale the actor will get painted on.
*
* Returns: TRUE if resource scale is set for the actor, otherwise FALSE
*/
gboolean
clutter_actor_get_resource_scale (ClutterActor *self,
gfloat *resource_scale)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
g_return_val_if_fail (resource_scale != NULL, FALSE);
if (_clutter_actor_get_real_resource_scale (self, resource_scale))
{
*resource_scale = ceilf (*resource_scale);
return TRUE;
}
return FALSE;
}
/**
* clutter_actor_has_overlaps:
* @self: A #ClutterActor

View file

@ -584,6 +584,11 @@ gboolean clutter_actor_is_in_clone_paint
CLUTTER_EXPORT
gboolean clutter_actor_get_paint_box (ClutterActor *self,
ClutterActorBox *box);
CLUTTER_EXPORT
gboolean clutter_actor_get_resource_scale (ClutterActor *self,
gfloat *resource_scale);
CLUTTER_EXPORT
gboolean clutter_actor_has_overlaps (ClutterActor *self);

View file

@ -49,6 +49,9 @@ void clutter_stage_freeze_updates (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_thaw_updates (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_update_resource_scales (ClutterStage *stage);
CLUTTER_EXPORT
gboolean clutter_actor_has_damage (ClutterActor *actor);

View file

@ -69,6 +69,9 @@ typedef struct _ClutterVertex4 ClutterVertex4;
#define CLUTTER_ACTOR_IN_REPARENT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_REPARENT) != FALSE)
#define CLUTTER_ACTOR_IN_PAINT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PAINT) != FALSE)
#define CLUTTER_ACTOR_IN_RELAYOUT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_RELAYOUT) != FALSE)
#define CLUTTER_ACTOR_IN_PREF_WIDTH(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PREF_WIDTH) != FALSE)
#define CLUTTER_ACTOR_IN_PREF_HEIGHT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PREF_HEIGHT) != FALSE)
#define CLUTTER_ACTOR_IN_PREF_SIZE(a) ((CLUTTER_PRIVATE_FLAGS (a) & (CLUTTER_IN_PREF_HEIGHT|CLUTTER_IN_PREF_WIDTH)) != FALSE)
#define CLUTTER_PARAM_READABLE (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)
#define CLUTTER_PARAM_WRITABLE (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)
@ -98,15 +101,17 @@ typedef enum
CLUTTER_IN_DESTRUCTION = 1 << 0,
CLUTTER_IS_TOPLEVEL = 1 << 1,
CLUTTER_IN_REPARENT = 1 << 2,
CLUTTER_IN_PREF_WIDTH = 1 << 3,
CLUTTER_IN_PREF_HEIGHT = 1 << 4,
/* Used to avoid recursion */
CLUTTER_IN_PAINT = 1 << 3,
CLUTTER_IN_PAINT = 1 << 5,
/* Used to avoid recursion */
CLUTTER_IN_RELAYOUT = 1 << 4,
CLUTTER_IN_RELAYOUT = 1 << 6,
/* a flag for internal children of Containers (DEPRECATED) */
CLUTTER_INTERNAL_CHILD = 1 << 5
CLUTTER_INTERNAL_CHILD = 1 << 7
} ClutterPrivateFlags;
/*

View file

@ -129,6 +129,8 @@ void _clutter_stage_presented (ClutterStage *stag
CoglFrameEvent frame_event,
ClutterFrameInfo *frame_info);
GList * _clutter_stage_peek_stage_views (ClutterStage *stage);
G_END_DECLS
#endif /* __CLUTTER_STAGE_PRIVATE_H__ */

View file

@ -5003,3 +5003,17 @@ clutter_stage_thaw_updates (ClutterStage *stage)
_clutter_master_clock_set_paused (master_clock, FALSE);
}
}
GList *
_clutter_stage_peek_stage_views (ClutterStage *stage)
{
ClutterStagePrivate *priv = stage->priv;
return _clutter_stage_window_get_views (priv->impl);
}
void
clutter_stage_update_resource_scales (ClutterStage *stage)
{
_clutter_actor_queue_update_resource_scale_recursive (CLUTTER_ACTOR (stage));
}

View file

@ -137,9 +137,11 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
ClutterActor *stage = meta_backend_get_stage (backend);
meta_renderer_rebuild_views (renderer);
meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer));
clutter_stage_update_resource_scales (CLUTTER_STAGE (stage));
ensure_frame_callbacks (stage_native);
}

View file

@ -68,7 +68,10 @@ meta_backend_x11_nested_update_screen_size (MetaBackend *backend,
MetaRenderer *renderer = meta_backend_get_renderer (backend);
if (meta_is_stage_views_enabled ())
meta_renderer_rebuild_views (renderer);
{
meta_renderer_rebuild_views (renderer);
clutter_stage_update_resource_scales (CLUTTER_STAGE (stage));
}
clutter_actor_set_size (stage, width, height);
}