diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h
index d1adcd38f..16ab012b6 100644
--- a/clutter/clutter/clutter-actor-private.h
+++ b/clutter/clutter/clutter-actor-private.h
@@ -288,7 +288,10 @@ float                           clutter_actor_get_real_resource_scale
 ClutterPaintNode *              clutter_actor_create_texture_paint_node                 (ClutterActor *self,
                                                                                          CoglTexture  *texture);
 
-void clutter_actor_update_stage_views (ClutterActor *self);
+void clutter_actor_update_stage_views (ClutterActor *self,
+                                       int           phase);
+
+void clutter_actor_queue_immediate_relayout (ClutterActor *self);
 
 G_END_DECLS
 
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
index e6a963139..86ffe2f1f 100644
--- a/clutter/clutter/clutter-actor.c
+++ b/clutter/clutter/clutter-actor.c
@@ -5903,6 +5903,25 @@ clutter_actor_real_has_overlaps (ClutterActor *self)
   return TRUE;
 }
 
+static float
+clutter_actor_real_calculate_resource_scale (ClutterActor *self,
+                                             int           phase)
+{
+  ClutterActorPrivate *priv = self->priv;
+  GList *l;
+  float new_resource_scale = -1.f;
+
+  for (l = priv->stage_views; l; l = l->next)
+    {
+      ClutterStageView *view = l->data;
+
+      new_resource_scale = MAX (clutter_stage_view_get_scale (view),
+                                new_resource_scale);
+    }
+
+  return new_resource_scale;
+}
+
 static void
 clutter_actor_real_destroy (ClutterActor *actor)
 {
@@ -5988,6 +6007,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
   klass->get_accessible = clutter_actor_real_get_accessible;
   klass->get_paint_volume = clutter_actor_real_get_paint_volume;
   klass->has_overlaps = clutter_actor_real_has_overlaps;
+  klass->calculate_resource_scale = clutter_actor_real_calculate_resource_scale;
   klass->paint = clutter_actor_real_paint;
   klass->destroy = clutter_actor_real_destroy;
 
@@ -16131,20 +16151,14 @@ out:
 }
 
 static void
-update_resource_scale (ClutterActor *self)
+update_resource_scale (ClutterActor *self,
+                       int           phase)
 {
   ClutterActorPrivate *priv = self->priv;
-  GList *l;
-  float new_resource_scale = -1.f;
-  float old_resource_scale;
+  float new_resource_scale, old_resource_scale;
 
-  for (l = priv->stage_views; l; l = l->next)
-    {
-      ClutterStageView *view = l->data;
-
-      new_resource_scale = MAX (clutter_stage_view_get_scale (view),
-                                new_resource_scale);
-    }
+  new_resource_scale =
+    CLUTTER_ACTOR_GET_CLASS (self)->calculate_resource_scale (self, phase);
 
   if (priv->resource_scale == new_resource_scale)
     return;
@@ -16168,7 +16182,8 @@ update_resource_scale (ClutterActor *self)
 }
 
 void
-clutter_actor_update_stage_views (ClutterActor *self)
+clutter_actor_update_stage_views (ClutterActor *self,
+                                  gboolean      use_max_scale)
 {
   ClutterActorPrivate *priv = self->priv;
   ClutterActor *child;
@@ -16181,12 +16196,12 @@ clutter_actor_update_stage_views (ClutterActor *self)
     return;
 
   update_stage_views (self);
-  update_resource_scale (self);
+  update_resource_scale (self, use_max_scale);
 
   priv->needs_update_stage_views = FALSE;
 
   for (child = priv->first_child; child; child = child->priv->next_sibling)
-    clutter_actor_update_stage_views (child);
+    clutter_actor_update_stage_views (child, use_max_scale);
 }
 
 /**
@@ -19710,3 +19725,17 @@ clutter_actor_has_accessible (ClutterActor *actor)
 
   return TRUE;
 }
+
+void
+clutter_actor_queue_immediate_relayout (ClutterActor *self)
+{
+  ClutterStage *stage;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+  clutter_actor_queue_relayout (self);
+
+  stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
+  if (stage)
+    clutter_stage_set_actor_needs_immediate_relayout (stage);
+}
diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h
index 9f795f6d3..67d327b5b 100644
--- a/clutter/clutter/clutter-actor.h
+++ b/clutter/clutter/clutter-actor.h
@@ -297,6 +297,8 @@ struct _ClutterActorClass
                                      ClutterTouchEvent    *event);
   gboolean (* has_accessible)       (ClutterActor         *self);
   void     (* resource_scale_changed) (ClutterActor *self);
+  float    (* calculate_resource_scale) (ClutterActor *self,
+                                         int           phase);
 
   /*< private >*/
   /* padding for future expansion */
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index 5d785b644..2ce0cd05b 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -136,6 +136,8 @@ void            clutter_stage_queue_actor_relayout      (ClutterStage *stage,
 GList * clutter_stage_get_views_for_rect (ClutterStage          *stage,
                                           const graphene_rect_t *rect);
 
+void clutter_stage_set_actor_needs_immediate_relayout (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 99d218441..8d3fc8b80 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -146,6 +146,7 @@ struct _ClutterStagePrivate
   guint min_size_changed       : 1;
   guint motion_events_enabled  : 1;
   guint stage_was_relayout     : 1;
+  guint actor_needs_immediate_relayout : 1;
 };
 
 enum
@@ -1354,8 +1355,34 @@ static void
 update_actor_stage_views (ClutterStage *stage)
 {
   ClutterActor *actor = CLUTTER_ACTOR (stage);
+  ClutterStagePrivate *priv = stage->priv;
+  int phase;
 
-  clutter_actor_update_stage_views (actor);
+  COGL_TRACE_BEGIN_SCOPED (ClutterStageUpdateActorStageViews,
+                           "Actor stage-views");
+
+  /* If an actor needs an immediate relayout because its resource scale
+   * changed, we give it another chance to allocate correctly before
+   * the paint.
+   *
+   * We're doing the whole thing twice and pass the phase to
+   * clutter_actor_update_stage_views() to allow actors to detect loops:
+   * If the resource scale changes again after the relayout, the new
+   * allocation of an actor probably moved the actor onto another stage
+   * view, so if an actor sees phase == 1, it can choose a "final" scale.
+   */
+  for (phase = 0; phase < 2; phase++)
+    {
+      clutter_actor_update_stage_views (actor, phase);
+
+      if (!priv->actor_needs_immediate_relayout)
+        break;
+
+      priv->actor_needs_immediate_relayout = FALSE;
+      _clutter_stage_maybe_relayout (actor);
+    }
+
+  g_warn_if_fail (!priv->actor_needs_immediate_relayout);
 }
 
 /**
@@ -1405,9 +1432,7 @@ _clutter_stage_do_update (ClutterStage *stage)
   if (stage_was_relayout)
     pointers = _clutter_stage_check_updated_pointers (stage);
 
-  COGL_TRACE_BEGIN (ClutterStageUpdateActorStageViews, "Actor stage-views");
   update_actor_stage_views (stage);
-  COGL_TRACE_END (ClutterStageUpdateActorStageViews);
 
   COGL_TRACE_BEGIN (ClutterStagePaint, "Paint");
 
@@ -4115,3 +4140,11 @@ clutter_stage_get_views_for_rect (ClutterStage          *stage,
 
   return views_for_rect;
 }
+
+void
+clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage)
+{
+  ClutterStagePrivate *priv = stage->priv;
+
+  priv->actor_needs_immediate_relayout = TRUE;
+}