From ad5555bf4286ece9844d919d62f8368dce90edf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 7 Apr 2017 14:06:36 +0200 Subject: [PATCH] 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 --- clutter/clutter/clutter-actor-private.h | 3 + clutter/clutter/clutter-actor.c | 251 +++++++++++++++++- clutter/clutter/clutter-actor.h | 5 + clutter/clutter/clutter-mutter.h | 3 + clutter/clutter/clutter-private.h | 11 +- clutter/clutter/clutter-stage-private.h | 2 + clutter/clutter/clutter-stage.c | 14 + src/backends/native/meta-stage-native.c | 2 + .../x11/nested/meta-backend-x11-nested.c | 5 +- 9 files changed, 287 insertions(+), 9 deletions(-) diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 7806894bc..c44f6342f 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -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); diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 43d58cd14..d0f7434ef 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -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 diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index ade912485..7d2168af1 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -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); diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h index 788757140..a53080457 100644 --- a/clutter/clutter/clutter-mutter.h +++ b/clutter/clutter/clutter-mutter.h @@ -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); diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h index 891a5612b..cbfaaa7f0 100644 --- a/clutter/clutter/clutter-private.h +++ b/clutter/clutter/clutter-private.h @@ -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; /* diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index dde8d4e21..688768bac 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -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__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index a10069eb1..67992d816 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -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)); +} diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c index c8afa752c..add3e81fd 100644 --- a/src/backends/native/meta-stage-native.c +++ b/src/backends/native/meta-stage-native.c @@ -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); } diff --git a/src/backends/x11/nested/meta-backend-x11-nested.c b/src/backends/x11/nested/meta-backend-x11-nested.c index 81a95bae8..172c4d9ac 100644 --- a/src/backends/x11/nested/meta-backend-x11-nested.c +++ b/src/backends/x11/nested/meta-backend-x11-nested.c @@ -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); }