[mutter] backends/native: Support drawing onto multiple onscreen framebuffers



commit aecd98b847a7fdb83b4449ef2a58a9d61a165d5d
Author: Jonas Ådahl <jadahl gmail com>
Date:   Wed Jun 8 17:05:31 2016 +0800

    backends/native: Support drawing onto multiple onscreen framebuffers
    
    Add support for drawing the stage using multiple stage views, where
    each stage view has its own onscreen framebuffer.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=768976

 src/backends/meta-backend-private.h            |    2 +
 src/backends/meta-backend.c                    |   16 +
 src/backends/meta-renderer-view.c              |   68 +++
 src/backends/meta-renderer-view.h              |    2 +
 src/backends/native/meta-backend-native.c      |    5 +-
 src/backends/native/meta-monitor-manager-kms.c |  171 +++----
 src/backends/native/meta-monitor-manager-kms.h |   22 +-
 src/backends/native/meta-renderer-native.c     |  622 ++++++++++++++++++------
 src/backends/native/meta-renderer-native.h     |   15 +-
 src/backends/native/meta-stage-native.c        |  233 +++++----
 src/backends/native/meta-stage-native.h        |    2 +
 11 files changed, 799 insertions(+), 359 deletions(-)
---
diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
index 5c65ff9..d727ba2 100644
--- a/src/backends/meta-backend-private.h
+++ b/src/backends/meta-backend-private.h
@@ -144,4 +144,6 @@ ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend);
 
 void meta_backend_monitors_changed (MetaBackend *backend);
 
+gboolean meta_is_stage_views_enabled (void);
+
 #endif /* META_BACKEND_PRIVATE_H */
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 1889fdb..2fbedcb 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -748,3 +748,19 @@ meta_clutter_init (void)
 
   meta_backend_post_init (_backend);
 }
+
+gboolean
+meta_is_stage_views_enabled (void)
+{
+  const gchar *mutter_stage_views;
+
+  if (!meta_is_wayland_compositor ())
+    return FALSE;
+
+  mutter_stage_views = g_getenv ("MUTTER_STAGE_VIEWS");
+
+  if (!mutter_stage_views)
+    return FALSE;
+
+  return strcmp (mutter_stage_views, "1") == 0;
+}
diff --git a/src/backends/meta-renderer-view.c b/src/backends/meta-renderer-view.c
index 6c9b2b2..6c90c16 100644
--- a/src/backends/meta-renderer-view.c
+++ b/src/backends/meta-renderer-view.c
@@ -22,6 +22,17 @@
 #include "backends/meta-renderer.h"
 #include "clutter/clutter-mutter.h"
 
+enum
+{
+  PROP_0,
+
+  PROP_MONITOR_INFO,
+
+  PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
 struct _MetaRendererView
 {
   ClutterStageView parent;
@@ -32,6 +43,50 @@ struct _MetaRendererView
 G_DEFINE_TYPE (MetaRendererView, meta_renderer_view,
                CLUTTER_TYPE_STAGE_VIEW_COGL)
 
+MetaMonitorInfo *
+meta_renderer_view_get_monitor_info (MetaRendererView *view)
+{
+  return view->monitor_info;
+}
+
+static void
+meta_renderer_view_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  MetaRendererView *view = META_RENDERER_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_MONITOR_INFO:
+      g_value_set_pointer (value, view->monitor_info);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+meta_renderer_view_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  MetaRendererView *view = META_RENDERER_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_MONITOR_INFO:
+      view->monitor_info = g_value_get_pointer (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
 static void
 meta_renderer_view_init (MetaRendererView *view)
 {
@@ -40,4 +95,17 @@ meta_renderer_view_init (MetaRendererView *view)
 static void
 meta_renderer_view_class_init (MetaRendererViewClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = meta_renderer_view_get_property;
+  object_class->set_property = meta_renderer_view_set_property;
+
+  obj_props[PROP_MONITOR_INFO] =
+    g_param_spec_pointer ("monitor-info",
+                          "MetaMonitorInfo",
+                          "The monitor info of the view",
+                          G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
 }
diff --git a/src/backends/meta-renderer-view.h b/src/backends/meta-renderer-view.h
index 0f08760..9c43fbb 100644
--- a/src/backends/meta-renderer-view.h
+++ b/src/backends/meta-renderer-view.h
@@ -26,4 +26,6 @@ G_DECLARE_FINAL_TYPE (MetaRendererView, meta_renderer_view,
                       META, RENDERER_VIEW,
                       ClutterStageViewCogl)
 
+MetaMonitorInfo *meta_renderer_view_get_monitor_info (MetaRendererView *view);
+
 #endif /* META_RENDERER_VIEW_H */
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 5edfd79..385122c 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -390,7 +390,10 @@ meta_backend_native_update_screen_size (MetaBackend *backend,
   ClutterActor *stage = meta_backend_get_stage (backend);
 
   stage_native = meta_clutter_backend_native_get_stage_native (clutter_backend);
-  meta_stage_native_legacy_set_size (stage_native, width, height);
+  if (meta_is_stage_views_enabled ())
+    meta_stage_native_rebuild_views (stage_native);
+  else
+    meta_stage_native_legacy_set_size (stage_native, width, height);
 
   clutter_actor_set_size (stage, width, height);
 }
diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
index 791d09b..04d96aa 100644
--- a/src/backends/native/meta-monitor-manager-kms.c
+++ b/src/backends/native/meta-monitor-manager-kms.c
@@ -78,14 +78,6 @@ typedef struct {
 
 typedef struct
 {
-  int pending;
-
-  MetaKmsFlipCallback callback;
-  void *user_data;
-} MetaKmsFlipClosure;
-
-typedef struct
-{
   GSource source;
 
   gpointer fd_tag;
@@ -1065,12 +1057,8 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
                                               unsigned int         n_outputs)
 {
   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  MetaBackend *backend;
-  MetaRenderer *renderer;
   unsigned i;
   int screen_width, screen_height;
-  gboolean ok;
-  GError *error;
 
   screen_width = 0; screen_height = 0;
   for (i = 0; i < n_crtcs; i++)
@@ -1156,20 +1144,6 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
       crtc->current_mode = NULL;
     }
 
-  backend = meta_get_backend ();
-  renderer = meta_backend_get_renderer (backend);
-
-  error = NULL;
-  ok = meta_renderer_native_set_layout (META_RENDERER_NATIVE (renderer),
-                                        screen_width, screen_height,
-                                        &error);
-  if (!ok)
-    {
-      meta_warning ("Applying display configuration failed: %s\n", error->message);
-      g_error_free (error);
-      return;
-    }
-
   for (i = 0; i < n_outputs; i++)
     {
       MetaOutputInfo *output_info = outputs[i];
@@ -1344,79 +1318,95 @@ get_crtc_connectors (MetaMonitorManager *manager,
 }
 
 void
-meta_monitor_manager_kms_apply_crtc_modes (MetaMonitorManagerKms *manager_kms,
-                                           uint32_t               fb_id)
+meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms,
+                                          MetaCRTC              *crtc,
+                                          int                    x,
+                                          int                    y,
+                                          uint32_t               fb_id)
 {
   MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
-  unsigned int i;
+  uint32_t *connectors;
+  unsigned int n_connectors;
+  drmModeModeInfo *mode;
 
-  for (i = 0; i < manager->n_crtcs; i++)
-    {
-      MetaCRTC *crtc = &manager->crtcs[i];
-      uint32_t *connectors;
-      unsigned int n_connectors;
-      drmModeModeInfo *mode;
+  get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
 
-      get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
+  if (connectors)
+    mode = crtc->current_mode->driver_private;
+  else
+    mode = NULL;
 
-      if (connectors)
-        mode = crtc->current_mode->driver_private;
-      else
-        mode = NULL;
+  if (drmModeSetCrtc (manager_kms->fd,
+                      crtc->crtc_id,
+                      fb_id,
+                      x, y,
+                      connectors, n_connectors,
+                      mode) != 0)
+    g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
 
-      if (drmModeSetCrtc (manager_kms->fd,
-                          crtc->crtc_id,
-                          fb_id,
-                          crtc->rect.x, crtc->rect.y,
-                          connectors, n_connectors,
-                          mode) != 0)
-        g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name);
+  g_free (connectors);
+}
 
-      g_free (connectors);
-    }
+static void
+invoke_flip_closure (GClosure *flip_closure)
+{
+  GValue param = G_VALUE_INIT;
+
+  g_value_init (&param, G_TYPE_POINTER);
+  g_value_set_pointer (&param, flip_closure);
+  g_closure_invoke (flip_closure, NULL, 1, &param, NULL);
+  g_closure_unref (flip_closure);
 }
 
 gboolean
-meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
-                                         uint32_t               fb_id,
-                                         MetaKmsFlipCallback    flip_callback,
-                                         void                  *user_data)
+meta_monitor_manager_kms_is_crtc_active (MetaMonitorManagerKms *manager_kms,
+                                         MetaCRTC              *crtc)
 {
   MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
-  MetaKmsFlipClosure *flip_closure = NULL;
   unsigned int i;
-  gboolean fb_in_use = FALSE;
+  gboolean connected_crtc_found;
 
   if (manager->power_save_mode != META_POWER_SAVE_ON)
     return FALSE;
 
-  for (i = 0; i < manager->n_crtcs; i++)
+  connected_crtc_found = FALSE;
+  for (i = 0; i < manager->n_outputs; i++)
     {
-      MetaCRTC *crtc = &manager->crtcs[i];
-      uint32_t *connectors;
-      unsigned int n_connectors;
-      int ret;
+      MetaOutput *output = &manager->outputs[i];
 
-      get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
+      if (output->crtc == crtc)
+        {
+          connected_crtc_found = TRUE;
+          break;
+        }
+    }
 
-      if (n_connectors == 0)
-        continue;
+  if (!connected_crtc_found)
+    return FALSE;
 
-      fb_in_use = TRUE;
+  return TRUE;
+}
 
-      if (manager_kms->page_flips_not_supported)
-        continue;
+gboolean
+meta_monitor_manager_kms_flip_crtc (MetaMonitorManagerKms *manager_kms,
+                                    MetaCRTC              *crtc,
+                                    int                    x,
+                                    int                    y,
+                                    uint32_t               fb_id,
+                                    GClosure              *flip_closure)
+{
+  MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
+  uint32_t *connectors;
+  unsigned int n_connectors;
+  int ret = -1;
 
-      if (!flip_closure)
-        {
-          flip_closure = g_new0 (MetaKmsFlipClosure, 1);
-          *flip_closure = (MetaKmsFlipClosure) {
-              .pending = 0,
-              .callback = flip_callback,
-              .user_data = user_data
-          };
-        }
+  g_assert (manager->power_save_mode == META_POWER_SAVE_ON);
+
+  get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
+  g_assert (n_connectors > 0);
 
+  if (!manager_kms->page_flips_not_supported)
+    {
       ret = drmModePageFlip (manager_kms->fd,
                              crtc->crtc_id,
                              fb_id,
@@ -1426,24 +1416,21 @@ meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
         {
           g_warning ("Failed to flip: %s", strerror (-ret));
           manager_kms->page_flips_not_supported = TRUE;
-          break;
         }
-
-      if (ret == 0)
-        flip_closure->pending++;
     }
 
-  if (manager_kms->page_flips_not_supported && fb_in_use)
-    {
-      /* If the driver doesn't support page flipping, just set the mode directly
-       * with the new framebuffer.
-       */
-      meta_monitor_manager_kms_apply_crtc_modes (manager_kms, fb_id);
-      flip_callback (user_data);
-      g_free (flip_closure);
-    }
+  if (manager_kms->page_flips_not_supported)
+    meta_monitor_manager_kms_apply_crtc_mode (manager_kms,
+                                              crtc,
+                                              x, y,
+                                              fb_id);
+
+  if (ret != 0)
+    return FALSE;
+
+  g_closure_ref (flip_closure);
 
-  return fb_in_use;
+  return TRUE;
 }
 
 static void
@@ -1453,11 +1440,9 @@ page_flip_handler (int fd,
                    unsigned int usec,
                    void *data)
 {
-  MetaKmsFlipClosure *flip_closure = data;
+  GClosure *flip_closure = data;
 
-  flip_closure->pending--;
-  if (flip_closure->pending == 0)
-    flip_closure->callback (flip_closure->user_data);
+  invoke_flip_closure (flip_closure);
 }
 
 void
diff --git a/src/backends/native/meta-monitor-manager-kms.h b/src/backends/native/meta-monitor-manager-kms.h
index 3f6251d..3386b93 100644
--- a/src/backends/native/meta-monitor-manager-kms.h
+++ b/src/backends/native/meta-monitor-manager-kms.h
@@ -39,13 +39,21 @@ GType meta_monitor_manager_kms_get_type (void);
 
 typedef void (*MetaKmsFlipCallback) (void *user_data);
 
-void meta_monitor_manager_kms_apply_crtc_modes (MetaMonitorManagerKms *manager_kms,
-                                                uint32_t               fb_id);
-
-gboolean meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
-                                                  uint32_t               fb_id,
-                                                  MetaKmsFlipCallback    flip_callback,
-                                                  void                  *user_data);
+void meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms,
+                                               MetaCRTC              *crtc,
+                                               int                    x,
+                                               int                    y,
+                                               uint32_t               fb_id);
+
+gboolean meta_monitor_manager_kms_is_crtc_active (MetaMonitorManagerKms *manager_kms,
+                                                  MetaCRTC              *crtc);
+
+gboolean meta_monitor_manager_kms_flip_crtc (MetaMonitorManagerKms *manager_kms,
+                                             MetaCRTC              *crtc,
+                                             int                    x,
+                                             int                    y,
+                                             uint32_t               fb_id,
+                                             GClosure              *flip_closure);
 
 void meta_monitor_manager_kms_wait_for_flip (MetaMonitorManagerKms *manager_kms);
 
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index b4a7aa3..e8d7451 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -47,9 +47,11 @@
 #include <xf86drm.h>
 
 #include "backends/meta-backend-private.h"
+#include "backends/meta-renderer-view.h"
 #include "backends/native/meta-monitor-manager-kms.h"
 #include "backends/native/meta-renderer-native.h"
 #include "cogl/cogl.h"
+#include "core/boxes-private.h"
 
 enum
 {
@@ -71,6 +73,11 @@ typedef struct _MetaOnscreenNative
 
   EGLSurface *pending_egl_surface;
   struct gbm_surface *pending_surface;
+
+  gboolean pending_set_crtc;
+
+  MetaRendererView *view;
+  int pending_flips;
 } MetaOnscreenNative;
 
 struct _MetaRendererNative
@@ -81,11 +88,9 @@ struct _MetaRendererNative
   struct gbm_device *gbm;
   CoglClosure *swap_notify_idle;
 
-  int width, height;
-  gboolean pending_set_crtc;
-  struct gbm_surface *dummy_gbm_surface;
+  int64_t frame_counter;
 
-  CoglOnscreen *onscreen;
+  struct gbm_surface *dummy_gbm_surface;
 };
 
 static void
@@ -112,11 +117,8 @@ meta_renderer_native_disconnect (CoglRenderer *cogl_renderer)
 }
 
 static void
-flush_pending_swap_notify_cb (void *data,
-                              void *user_data)
+flush_pending_swap_notify (CoglFramebuffer *framebuffer)
 {
-  CoglFramebuffer *framebuffer = data;
-
   if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
     {
       CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
@@ -130,7 +132,9 @@ flush_pending_swap_notify_cb (void *data,
 
           _cogl_onscreen_notify_frame_sync (onscreen, info);
           _cogl_onscreen_notify_complete (onscreen, info);
+
           onscreen_native->pending_swap_notify = FALSE;
+          cogl_object_unref (onscreen);
 
           cogl_object_unref (info);
         }
@@ -143,15 +147,23 @@ flush_pending_swap_notify_idle (void *user_data)
   CoglContext *cogl_context = user_data;
   CoglRendererEGL *egl_renderer = cogl_context->display->renderer->winsys;
   MetaRendererNative *renderer_native = egl_renderer->platform;
+  GList *l;
 
   /* This needs to be disconnected before invoking the callbacks in
    * case the callbacks cause it to be queued again */
   _cogl_closure_disconnect (renderer_native->swap_notify_idle);
   renderer_native->swap_notify_idle = NULL;
 
-  g_list_foreach (cogl_context->framebuffers,
-                  flush_pending_swap_notify_cb,
-                  NULL);
+  l = cogl_context->framebuffers;
+  while (l)
+    {
+      GList *next = l->next;
+      CoglFramebuffer *framebuffer = l->data;
+
+      flush_pending_swap_notify (framebuffer);
+
+      l = next;
+    }
 }
 
 static void
@@ -179,11 +191,14 @@ free_current_bo (CoglOnscreen *onscreen)
 }
 
 static void
-queue_swap_notify_for_onscreen (CoglOnscreen *onscreen)
+meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
 {
-  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenEGL *egl_onscreen =  onscreen->winsys;
   MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
-  CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context;
+  MetaBackend *backend = meta_get_backend ();
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context =
+    clutter_backend_get_cogl_context (clutter_backend);
   CoglRenderer *cogl_renderer = cogl_context->display->renderer;
   CoglRendererEGL *egl_renderer = cogl_renderer->winsys;
   MetaRendererNative *renderer_native = egl_renderer->platform;
@@ -200,6 +215,13 @@ queue_swap_notify_for_onscreen (CoglOnscreen *onscreen)
                                       NULL);
     }
 
+  /*
+   * The framebuffer will have its own referenc while the swap notify is
+   * pending. Otherwise when destroying the view would drop the pending
+   * notification with if the destruction happens before the idle callback
+   * is invoked.
+   */
+  cogl_object_ref (onscreen);
   onscreen_native->pending_swap_notify = TRUE;
 }
 
@@ -248,18 +270,6 @@ fail:
   return FALSE;
 }
 
-static void
-setup_crtc_modes (CoglDisplay *cogl_display, int fb_id)
-{
-  MetaBackend *backend = meta_get_backend ();
-  MetaMonitorManager *monitor_manager =
-    meta_backend_get_monitor_manager (backend);
-  MetaMonitorManagerKms *monitor_manager_kms =
-    META_MONITOR_MANAGER_KMS (monitor_manager);
-
-  meta_monitor_manager_kms_apply_crtc_modes (monitor_manager_kms, fb_id);
-}
-
 static CoglBool
 meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display,
                                         CoglError  **error)
@@ -273,7 +283,7 @@ meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display,
   /* Force a full modeset / drmModeSetCrtc on
    * the first swap buffers call.
    */
-  renderer_native->pending_set_crtc = TRUE;
+  meta_renderer_native_queue_modes_reset (renderer_native);
 
   return TRUE;
 }
@@ -352,14 +362,11 @@ meta_renderer_native_egl_cleanup_context (CoglDisplay *cogl_display)
 }
 
 static void
-flip_callback (void *user_data)
+meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
 {
-  CoglOnscreen *onscreen = user_data;
-  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenEGL *egl_onscreen =  onscreen->winsys;
   MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
 
-  queue_swap_notify_for_onscreen (onscreen);
-
   free_current_bo (onscreen);
 
   onscreen_native->current_fb_id = onscreen_native->next_fb_id;
@@ -367,8 +374,205 @@ flip_callback (void *user_data)
 
   onscreen_native->current_bo = onscreen_native->next_bo;
   onscreen_native->next_bo = NULL;
+}
 
-  cogl_object_unref (onscreen);
+static void
+on_crtc_flipped (GClosure         *closure,
+                 MetaRendererView *view)
+{
+  ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
+  CoglFramebuffer *framebuffer =
+    clutter_stage_view_get_framebuffer (stage_view);
+  CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+  CoglOnscreenEGL *egl_onscreen =  onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
+
+  onscreen_native->pending_flips--;
+  if (onscreen_native->pending_flips == 0)
+    {
+      meta_onscreen_native_queue_swap_notify (onscreen);
+      meta_onscreen_native_swap_drm_fb (onscreen);
+    }
+}
+
+static void
+flip_closure_destroyed (MetaRendererView *view)
+{
+  ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
+  CoglFramebuffer *framebuffer =
+    clutter_stage_view_get_framebuffer (stage_view);
+  CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+  CoglOnscreenEGL *egl_onscreen =  onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
+
+  if (onscreen_native->next_fb_id)
+    {
+      MetaBackend *backend = meta_get_backend ();
+      MetaRenderer *renderer = meta_backend_get_renderer (backend);
+      MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
+
+      drmModeRmFB (renderer_native->kms_fd, onscreen_native->next_fb_id);
+      gbm_surface_release_buffer (onscreen_native->surface,
+                                  onscreen_native->next_bo);
+      onscreen_native->next_bo = NULL;
+      onscreen_native->next_fb_id = 0;
+
+      meta_onscreen_native_queue_swap_notify (onscreen);
+    }
+
+  g_object_unref (view);
+}
+
+static void
+meta_onscreen_native_flip_crtc (MetaOnscreenNative *onscreen_native,
+                                GClosure           *flip_closure,
+                                MetaCRTC           *crtc,
+                                int                 x,
+                                int                 y,
+                                gboolean           *fb_in_use)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  MetaMonitorManagerKms *monitor_manager_kms =
+    META_MONITOR_MANAGER_KMS (monitor_manager);
+
+  if (!meta_monitor_manager_kms_is_crtc_active (monitor_manager_kms,
+                                                crtc))
+    {
+      *fb_in_use = FALSE;
+      return;
+    }
+
+  if (meta_monitor_manager_kms_flip_crtc (monitor_manager_kms,
+                                          crtc,
+                                          x, y,
+                                          onscreen_native->next_fb_id,
+                                          flip_closure))
+    onscreen_native->pending_flips++;
+
+  *fb_in_use = TRUE;
+}
+
+static void
+meta_onscreen_native_set_crtc_modes (MetaOnscreenNative *onscreen_native)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  MetaMonitorManagerKms *monitor_manager_kms =
+    META_MONITOR_MANAGER_KMS (monitor_manager);
+  MetaRendererView *view = onscreen_native->view;
+  uint32_t next_fb_id = onscreen_native->next_fb_id;
+  MetaMonitorInfo *monitor_info;
+
+  monitor_info = meta_renderer_view_get_monitor_info (view);
+  if (monitor_info)
+    {
+      int i;
+
+      for (i = 0; i < monitor_info->n_outputs; i++)
+        {
+          MetaOutput *output = monitor_info->outputs[i];
+          int x = output->crtc->rect.x - monitor_info->rect.x;
+          int y = output->crtc->rect.y - monitor_info->rect.y;
+
+          meta_monitor_manager_kms_apply_crtc_mode (monitor_manager_kms,
+                                                    output->crtc,
+                                                    x, y,
+                                                    next_fb_id);
+        }
+    }
+  else
+    {
+      unsigned int i;
+
+      for (i = 0; i < monitor_manager->n_crtcs; i++)
+        {
+          MetaCRTC *crtc = &monitor_manager->crtcs[i];
+
+          meta_monitor_manager_kms_apply_crtc_mode (monitor_manager_kms,
+                                                    crtc,
+                                                    crtc->rect.x, crtc->rect.y,
+                                                    next_fb_id);
+        }
+    }
+}
+
+static void
+meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
+{
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
+  MetaBackend *backend = meta_get_backend ();
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  MetaRendererView *view = onscreen_native->view;
+  GClosure *flip_closure;
+  MetaMonitorInfo *monitor_info;
+  gboolean fb_in_use = FALSE;
+
+  /*
+   * Create a closure that either will be invoked or destructed.
+   * Invoking the closure represents a completed flip. If the closure
+   * is destructed before being invoked, the framebuffer references will be
+   * cleaned up accordingly.
+   *
+   * Each successful flip will each own one reference to the closure, thus keep
+   * it alive until either invoked or destructed. If flipping failed, the
+   * closure will be destructed before this function goes out of scope.
+   */
+  flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped),
+                                 g_object_ref (view),
+                                 (GClosureNotify) flip_closure_destroyed);
+  g_closure_set_marshal (flip_closure, g_cclosure_marshal_generic);
+
+  /* Either flip the CRTC's of the monitor info, if we are drawing just part
+   * of the stage, or all of the CRTC's if we are drawing the whole stage.
+   */
+  monitor_info = meta_renderer_view_get_monitor_info (view);
+  if (monitor_info)
+    {
+      int i;
+
+      for (i = 0; i < monitor_info->n_outputs; i++)
+        {
+          MetaOutput *output = monitor_info->outputs[i];
+          int x = output->crtc->rect.x - monitor_info->rect.x;
+          int y = output->crtc->rect.y - monitor_info->rect.y;
+
+          meta_onscreen_native_flip_crtc (onscreen_native, flip_closure,
+                                          output->crtc, x, y,
+                                          &fb_in_use);
+        }
+    }
+  else
+    {
+      unsigned int i;
+
+      for (i = 0; i < monitor_manager->n_crtcs; i++)
+        {
+          MetaCRTC *crtc = &monitor_manager->crtcs[i];
+
+          meta_onscreen_native_flip_crtc (onscreen_native, flip_closure,
+                                          crtc, crtc->rect.x, crtc->rect.y,
+                                          &fb_in_use);
+        }
+    }
+
+  /*
+   * If the framebuffer is in use, but we don't have any pending flips it means
+   * that flipping is not supported and we set the next framebuffer directly.
+   * Since we won't receive a flip callback, lets just notify listeners
+   * directly.
+   */
+  if (fb_in_use && onscreen_native->pending_flips == 0)
+    {
+      meta_onscreen_native_queue_swap_notify (onscreen);
+      meta_onscreen_native_swap_drm_fb (onscreen);
+    }
+
+  g_closure_unref (flip_closure);
 }
 
 static void
@@ -387,27 +591,35 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
   MetaRendererNative *renderer_native = egl_renderer->platform;
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
+  CoglFrameInfo *frame_info;
+  MetaRendererView *view;
+  cairo_rectangle_int_t view_layout;
   uint32_t handle, stride;
-  gboolean fb_in_use;
-  uint32_t next_fb_id;
+
+  frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos);
+  frame_info->global_frame_counter = renderer_native->frame_counter;
 
   /* If we already have a pending swap then block until it completes */
   while (onscreen_native->next_fb_id != 0)
     meta_monitor_manager_kms_wait_for_flip (monitor_manager_kms);
 
+  view = onscreen_native->view;
+  clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout);
+
   if (onscreen_native->pending_egl_surface)
     {
-      CoglFramebuffer *fb = COGL_FRAMEBUFFER (renderer_native->onscreen);
+      CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
 
       eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface);
       egl_onscreen->egl_surface = onscreen_native->pending_egl_surface;
       onscreen_native->pending_egl_surface = NULL;
 
       _cogl_framebuffer_winsys_update_size (fb,
-                                            renderer_native->width,
-                                            renderer_native->height);
+                                            view_layout.width,
+                                            view_layout.height);
       cogl_context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND;
     }
+
   parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
                                                     rectangles,
                                                     n_rectangles);
@@ -420,6 +632,7 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
       onscreen_native->surface = onscreen_native->pending_surface;
       onscreen_native->pending_surface = NULL;
     }
+
   /* Now we need to set the CRTC to whatever is the front buffer */
   onscreen_native->next_bo =
     gbm_surface_lock_front_buffer (onscreen_native->surface);
@@ -428,8 +641,8 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
   handle = gbm_bo_get_handle (onscreen_native->next_bo).u32;
 
   if (drmModeAddFB (renderer_native->kms_fd,
-                    renderer_native->width,
-                    renderer_native->height,
+                    view_layout.width,
+                    view_layout.height,
                     24, /* depth */
                     32, /* bpp */
                     stride,
@@ -446,34 +659,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
 
   /* If this is the first framebuffer to be presented then we now setup the
    * crtc modes, else we flip from the previous buffer */
-  if (renderer_native->pending_set_crtc)
+  if (onscreen_native->pending_set_crtc)
     {
-      setup_crtc_modes (cogl_context->display, onscreen_native->next_fb_id);
-      renderer_native->pending_set_crtc = FALSE;
+      meta_onscreen_native_set_crtc_modes (onscreen_native);
+      onscreen_native->pending_set_crtc = FALSE;
     }
 
-  next_fb_id = onscreen_native->next_fb_id;
-
-  /* Reference will either be released in flip_callback, or if the fb
-   * wasn't used, indicated by the return value below.
-   */
-  cogl_object_ref (onscreen);
-  fb_in_use = meta_monitor_manager_kms_flip_all_crtcs (monitor_manager_kms,
-                                                       next_fb_id,
-                                                       flip_callback, onscreen);
-
-  if (!fb_in_use)
-    {
-      drmModeRmFB (renderer_native->kms_fd, onscreen_native->next_fb_id);
-      gbm_surface_release_buffer (onscreen_native->surface,
-                                  onscreen_native->next_bo);
-      onscreen_native->next_bo = NULL;
-      onscreen_native->next_fb_id = 0;
-
-      queue_swap_notify_for_onscreen (onscreen);
-
-      g_object_unref (onscreen);
-    }
+  meta_onscreen_native_flip_crtcs (onscreen);
 }
 
 static CoglBool
@@ -489,6 +681,62 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context,
   COGL_FLAGS_SET (cogl_context->winsys_features,
                   COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
                   TRUE);
+  COGL_FLAGS_SET (cogl_context->winsys_features,
+                  COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
+                  TRUE);
+
+  return TRUE;
+}
+
+static gboolean
+meta_renderer_native_create_surface (MetaRendererNative  *renderer_native,
+                                     int                  width,
+                                     int                  height,
+                                     struct gbm_surface **gbm_surface,
+                                     EGLSurface          *egl_surface,
+                                     GError             **error)
+{
+  MetaBackend *backend = meta_get_backend ();
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context =
+    clutter_backend_get_cogl_context (clutter_backend);
+  CoglDisplay *cogl_display = cogl_context->display;
+  CoglDisplayEGL *egl_display = cogl_display->winsys;
+  CoglRendererEGL *egl_renderer = cogl_display->renderer->winsys;
+  struct gbm_surface *new_gbm_surface;
+  EGLNativeWindowType egl_native_window;
+  EGLSurface new_egl_surface;
+
+  new_gbm_surface = gbm_surface_create (renderer_native->gbm,
+                                        width, height,
+                                        GBM_FORMAT_XRGB8888,
+                                        GBM_BO_USE_SCANOUT |
+                                        GBM_BO_USE_RENDERING);
+
+  if (!new_gbm_surface)
+    {
+      g_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                   "Failed to allocate surface");
+      return FALSE;
+    }
+
+  egl_native_window = (EGLNativeWindowType) new_gbm_surface;
+  new_egl_surface = eglCreateWindowSurface (egl_renderer->edpy,
+                                            egl_display->egl_config,
+                                            egl_native_window,
+                                            NULL);
+  if (new_egl_surface == EGL_NO_SURFACE)
+    {
+      gbm_surface_destroy (new_gbm_surface);
+      g_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                   "Failed to allocate surface");
+      return FALSE;
+    }
+
+  *gbm_surface = new_gbm_surface;
+  *egl_surface = new_egl_surface;
 
   return TRUE;
 }
@@ -506,66 +754,38 @@ meta_renderer_native_init_onscreen (CoglOnscreen *onscreen,
   MetaRendererNative *renderer_native = egl_renderer->platform;
   CoglOnscreenEGL *egl_onscreen;
   MetaOnscreenNative *onscreen_native;
+  int width;
+  int height;
 
   _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
 
-  if (renderer_native->onscreen)
-    {
-      _cogl_set_error (error, COGL_WINSYS_ERROR,
-                       COGL_WINSYS_ERROR_CREATE_ONSCREEN,
-                       "Cannot have multiple onscreens in the KMS platform");
-      return FALSE;
-    }
-
-  renderer_native->onscreen = onscreen;
-
   onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
   egl_onscreen = onscreen->winsys;
 
   onscreen_native = g_slice_new0 (MetaOnscreenNative);
   egl_onscreen->platform = onscreen_native;
 
+  onscreen_native->pending_set_crtc = TRUE;
+
   /* If a kms_fd is set then the display width and height
    * won't be available until meta_renderer_native_set_layout
    * is called. In that case, defer creating the surface
    * until then.
    */
-  if (renderer_native->width == 0 ||
-      renderer_native->height == 0)
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+  if (width == 0 || height == 0)
     return TRUE;
 
-  onscreen_native->surface =
-    gbm_surface_create (renderer_native->gbm,
-                        renderer_native->width,
-                        renderer_native->height,
-                        GBM_FORMAT_XRGB8888,
-                        GBM_BO_USE_SCANOUT |
-                        GBM_BO_USE_RENDERING);
-
-  if (!onscreen_native->surface)
-    {
-      _cogl_set_error (error, COGL_WINSYS_ERROR,
-                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
-                   "Failed to allocate surface");
-      return FALSE;
-    }
+  if (!meta_renderer_native_create_surface (renderer_native,
+                                            width, height,
+                                            &onscreen_native->surface,
+                                            &egl_onscreen->egl_surface,
+                                            error))
+    return FALSE;
 
-  egl_onscreen->egl_surface =
-    eglCreateWindowSurface (egl_renderer->edpy,
-                            egl_display->egl_config,
-                            (EGLNativeWindowType) onscreen_native->surface,
-                            NULL);
-  if (egl_onscreen->egl_surface == EGL_NO_SURFACE)
-    {
-      _cogl_set_error (error, COGL_WINSYS_ERROR,
-                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
-                   "Failed to allocate surface");
-      return FALSE;
-    }
 
-  _cogl_framebuffer_winsys_update_size (framebuffer,
-                                        renderer_native->width,
-                                        renderer_native->height);
+  _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
 
   return TRUE;
 }
@@ -575,9 +795,6 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
 {
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
   CoglContext *cogl_context = framebuffer->context;
-  CoglDisplay *cogl_display = cogl_context->display;
-  CoglDisplayEGL *egl_display = cogl_display->winsys;
-  MetaRendererNative *renderer_native = egl_display->platform;
   CoglRenderer *cogl_renderer = cogl_context->display->renderer;
   CoglRendererEGL *egl_renderer = cogl_renderer->winsys;
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
@@ -587,8 +804,6 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
   if (egl_onscreen == NULL)
     return;
 
-  renderer_native->onscreen = NULL;
-
   onscreen_native = egl_onscreen->platform;
 
   /* flip state takes a reference on the onscreen so there should
@@ -638,60 +853,57 @@ meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native)
 void
 meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native)
 {
-  renderer_native->pending_set_crtc = TRUE;
+  MetaRenderer *renderer = META_RENDERER (renderer_native);
+  GList *l;
+
+  for (l = meta_renderer_get_views (renderer); l; l = l->next)
+    {
+      ClutterStageView *stage_view = l->data;
+      CoglFramebuffer *framebuffer =
+        clutter_stage_view_get_framebuffer (stage_view);
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+      MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
+
+      onscreen_native->pending_set_crtc = TRUE;
+    }
 }
 
 gboolean
-meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
-                                 int                 width,
-                                 int                 height,
-                                 GError            **error)
+meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native,
+                                           MetaRendererView   *view,
+                                           int                 width,
+                                           int                 height,
+                                           GError            **error)
 {
   ClutterBackend *clutter_backend = clutter_get_default_backend ();
   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
   CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
-  CoglDisplayEGL *egl_display = cogl_display->winsys;
-  CoglRenderer *cogl_renderer = cogl_display->renderer;
-  CoglRendererEGL *egl_renderer = cogl_renderer->winsys;
+  CoglRendererEGL *egl_renderer = cogl_display->renderer->winsys;
+  ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
+  cairo_rectangle_int_t view_layout;
 
-  if ((width != renderer_native->width ||
-       height != renderer_native->height) &&
-      renderer_native->onscreen)
+  clutter_stage_view_get_layout (stage_view, &view_layout);
+
+  if (width != view_layout.width || height != view_layout.height)
     {
-      CoglOnscreenEGL *egl_onscreen = renderer_native->onscreen->winsys;
+      CoglFramebuffer *framebuffer =
+        clutter_stage_view_get_framebuffer (stage_view);
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
       MetaOnscreenNative *onscreen_native = egl_onscreen->platform;
       struct gbm_surface *new_surface;
       EGLSurface new_egl_surface;
 
       /* Need to drop the GBM surface and create a new one */
 
-      new_surface = gbm_surface_create (renderer_native->gbm,
-                                        width, height,
-                                        GBM_FORMAT_XRGB8888,
-                                        GBM_BO_USE_SCANOUT |
-                                        GBM_BO_USE_RENDERING);
-
-      if (!new_surface)
-        {
-          _cogl_set_error (error, COGL_WINSYS_ERROR,
-                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
-                           "Failed to allocate new surface");
-          return FALSE;
-        }
+      if (!meta_renderer_native_create_surface (renderer_native,
+                                                width, height,
+                                                &new_surface,
+                                                &new_egl_surface,
+                                                error))
+        return FALSE;
 
-      new_egl_surface =
-        eglCreateWindowSurface (egl_renderer->edpy,
-                                egl_display->egl_config,
-                                (EGLNativeWindowType) new_surface,
-                                NULL);
-      if (new_egl_surface == EGL_NO_SURFACE)
-        {
-          _cogl_set_error (error, COGL_WINSYS_ERROR,
-                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
-                           "Failed to allocate new surface");
-          gbm_surface_destroy (new_surface);
-          return FALSE;
-        }
 
       if (onscreen_native->pending_egl_surface)
         eglDestroySurface (egl_renderer->edpy,
@@ -710,8 +922,6 @@ meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
         }
       else
         {
-          CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (renderer_native->onscreen);
-
           onscreen_native->surface = new_surface;
           egl_onscreen->egl_surface = new_egl_surface;
 
@@ -719,10 +929,7 @@ meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
         }
     }
 
-  renderer_native->width = width;
-  renderer_native->height = height;
-
-  renderer_native->pending_set_crtc = TRUE;
+  meta_renderer_native_queue_modes_reset (renderer_native);
 
   return TRUE;
 }
@@ -774,6 +981,108 @@ meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
 }
 
 static void
+meta_onscreen_native_set_view (CoglOnscreen     *onscreen,
+                               MetaRendererView *view)
+{
+  CoglOnscreenEGL *egl_onscreen;
+  MetaOnscreenNative *onscreen_native;
+
+  egl_onscreen = onscreen->winsys;
+  onscreen_native = egl_onscreen->platform;
+  onscreen_native->view = view;
+}
+
+MetaRendererView *
+meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  CoglOnscreen *onscreen;
+  CoglFramebuffer *framebuffer;
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  cairo_rectangle_int_t view_layout = { 0 };
+  MetaRendererView *view;
+  GError *error = NULL;
+
+  if (!monitor_manager)
+    return NULL;
+
+  meta_monitor_manager_get_screen_size (monitor_manager,
+                                        &view_layout.width,
+                                        &view_layout.height);
+
+  onscreen = cogl_onscreen_new (cogl_context,
+                                view_layout.width,
+                                view_layout.height);
+  cogl_onscreen_set_swap_throttled (onscreen,
+                                    _clutter_get_sync_to_vblank ());
+
+  framebuffer = COGL_FRAMEBUFFER (onscreen);
+  if (!cogl_framebuffer_allocate (framebuffer, &error))
+    meta_fatal ("Failed to allocate onscreen framebuffer: %s\n",
+                error->message);
+
+  view = g_object_new (META_TYPE_RENDERER_VIEW,
+                       "layout", &view_layout,
+                       "framebuffer", framebuffer,
+                       NULL);
+
+  meta_onscreen_native_set_view (onscreen, view);
+
+  return view;
+}
+
+static MetaRendererView *
+meta_renderer_native_create_view (MetaRenderer    *renderer,
+                                  MetaMonitorInfo *monitor_info)
+{
+  MetaBackend *backend = meta_get_backend ();
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  CoglOnscreen *onscreen;
+  CoglFramebuffer *framebuffer;
+  cairo_rectangle_int_t view_layout;
+  MetaRendererView *view;
+  GError *error = NULL;
+
+  view_layout = meta_rectangle_to_cairo_rectangle (&monitor_info->rect);
+  onscreen = cogl_onscreen_new (cogl_context,
+                                view_layout.width,
+                                view_layout.height);
+  cogl_onscreen_set_swap_throttled (onscreen,
+                                    _clutter_get_sync_to_vblank ());
+
+  framebuffer = COGL_FRAMEBUFFER (onscreen);
+  if (!cogl_framebuffer_allocate (framebuffer, &error))
+    meta_fatal ("Failed to allocate onscreen framebuffer: %s\n",
+                error->message);
+
+  view = g_object_new (META_TYPE_RENDERER_VIEW,
+                       "layout", &view_layout,
+                       "framebuffer", framebuffer,
+                       "monitor-info", monitor_info,
+                       NULL);
+
+  meta_onscreen_native_set_view (onscreen, view);
+
+  return view;
+}
+
+void
+meta_renderer_native_finish_frame (MetaRendererNative *renderer_native)
+{
+  renderer_native->frame_counter++;
+}
+
+int64_t
+meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native)
+{
+  return renderer_native->frame_counter;
+}
+
+static void
 meta_renderer_native_get_property (GObject    *object,
                                    guint       prop_id,
                                    GValue     *value,
@@ -879,6 +1188,7 @@ meta_renderer_native_class_init (MetaRendererNativeClass *klass)
   object_class->finalize = meta_renderer_native_finalize;
 
   renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer;
+  renderer_class->create_view = meta_renderer_native_create_view;
 
   g_object_class_install_property (object_class,
                                    PROP_KMS_FD,
diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h
index f7ef7bc..37ea9e3 100644
--- a/src/backends/native/meta-renderer-native.h
+++ b/src/backends/native/meta-renderer-native.h
@@ -44,13 +44,20 @@ int meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native);
 
 void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
 
-gboolean meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
-                                          int                 width,
-                                          int                 height,
-                                          GError            **error);
+gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native,
+                                                    MetaRendererView   *view,
+                                                    int                 width,
+                                                    int                 height,
+                                                    GError            **error);
 
 void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,
                                            uint32_t            id,
                                            gboolean            ignore);
 
+MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native);
+
+void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native);
+
+int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native);
+
 #endif /* META_RENDERER_NATIVE_H */
diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c
index 624c514..c0b6d24 100644
--- a/src/backends/native/meta-stage-native.c
+++ b/src/backends/native/meta-stage-native.c
@@ -27,16 +27,22 @@
 #include "backends/native/meta-stage-native.h"
 
 #include "backends/meta-backend-private.h"
+#include "backends/native/meta-renderer-native.h"
 #include "meta/meta-backend.h"
 #include "meta/meta-monitor-manager.h"
 #include "meta/util.h"
 
+static GQuark quark_view_frame_closure  = 0;
+
 struct _MetaStageNative
 {
   ClutterStageCogl parent;
 
   CoglOnscreen *pending_onscreen;
   CoglClosure *frame_closure;
+
+  int64_t presented_frame_counter_sync;
+  int64_t presented_frame_counter_complete;
 };
 
 static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
@@ -63,41 +69,123 @@ get_legacy_view (MetaRenderer *renderer)
     return NULL;
 }
 
-static CoglOnscreen *
-get_legacy_onscreen (MetaStageNative *stage_native)
+static void
+frame_cb (CoglOnscreen  *onscreen,
+          CoglFrameEvent frame_event,
+          CoglFrameInfo *frame_info,
+          void          *user_data)
+
 {
-  if (stage_native->pending_onscreen)
+  MetaStageNative *stage_native = user_data;
+  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_native);
+  int64_t global_frame_counter;
+  int64_t presented_frame_counter;
+  ClutterFrameInfo clutter_frame_info;
+
+  global_frame_counter = cogl_frame_info_get_global_frame_counter (frame_info);
+
+  switch (frame_event)
     {
-      return stage_native->pending_onscreen;
+    case COGL_FRAME_EVENT_SYNC:
+      presented_frame_counter = stage_native->presented_frame_counter_sync;
+      stage_native->presented_frame_counter_sync = global_frame_counter;
+      break;
+    case COGL_FRAME_EVENT_COMPLETE:
+      presented_frame_counter = stage_native->presented_frame_counter_complete;
+      stage_native->presented_frame_counter_complete = global_frame_counter;
+      break;
+    default:
+      g_assert_not_reached ();
     }
-  else
-    {
-      MetaBackend *backend = meta_get_backend ();
-      MetaRenderer *renderer = meta_backend_get_renderer (backend);
-      ClutterStageView *stage_view;
-      CoglFramebuffer *framebuffer;
 
-      stage_view = CLUTTER_STAGE_VIEW (get_legacy_view (renderer));
-      framebuffer = clutter_stage_view_get_framebuffer (stage_view);
+  if (global_frame_counter <= presented_frame_counter)
+    return;
+
+  clutter_frame_info = (ClutterFrameInfo) {
+    .frame_counter = global_frame_counter,
+    .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
+    .presentation_time = cogl_frame_info_get_presentation_time (frame_info)
+  };
+
+  _clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
+}
+
+static void
+ensure_frame_callback (MetaStageNative  *stage_native,
+                       ClutterStageView *stage_view)
+{
+  CoglFramebuffer *framebuffer;
+  CoglOnscreen *onscreen;
+  CoglClosure *closure;
+
+  closure = g_object_get_qdata (G_OBJECT (stage_view),
+                                quark_view_frame_closure);
+  if (closure)
+    return;
+
+  framebuffer = clutter_stage_view_get_framebuffer (stage_view);
+  onscreen = COGL_ONSCREEN (framebuffer);
+  closure = cogl_onscreen_add_frame_callback (onscreen,
+                                              frame_cb,
+                                              stage_native,
+                                              NULL);
+  g_object_set_qdata (G_OBJECT (stage_view),
+                      quark_view_frame_closure,
+                      closure);
+}
+
+static void
+ensure_frame_callbacks (MetaStageNative *stage_native)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
+  GList *l;
+
+  for (l = meta_renderer_get_views (renderer); l; l = l->next)
+    {
+      ClutterStageView *stage_view = l->data;
 
-      return COGL_ONSCREEN (framebuffer);
+      ensure_frame_callback (stage_native, stage_view);
     }
 }
 
 void
+meta_stage_native_rebuild_views (MetaStageNative *stage_native)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
+
+  meta_renderer_rebuild_views (renderer);
+  ensure_frame_callbacks (stage_native);
+}
+
+void
 meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
                                    int              width,
                                    int              height)
 {
   MetaBackend *backend = meta_get_backend ();
   MetaRenderer *renderer = meta_backend_get_renderer (backend);
+  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
   MetaRendererView *legacy_view;
+  GError *error = NULL;
   cairo_rectangle_int_t view_layout;
 
   legacy_view = get_legacy_view (renderer);
   if (!legacy_view)
     return;
 
+  if (!meta_renderer_native_set_legacy_view_size (renderer_native,
+                                                  legacy_view,
+                                                  width, height,
+                                                  &error))
+    {
+      meta_warning ("Applying display configuration failed: %s\n",
+                    error->message);
+      g_error_free (error);
+      return;
+    }
+
   view_layout = (cairo_rectangle_int_t) {
     .width = width,
     .height = height
@@ -107,29 +195,6 @@ meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
                 NULL);
 }
 
-static gboolean
-meta_stage_native_realize (ClutterStageWindow *stage_window)
-{
-  MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
-  MetaBackend *backend = meta_get_backend ();
-  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
-  CoglFramebuffer *framebuffer;
-  GError *error = NULL;
-
-  stage_native->pending_onscreen =
-    cogl_onscreen_new (clutter_backend->cogl_context, 1, 1);
-
-  framebuffer = COGL_FRAMEBUFFER (stage_native->pending_onscreen);
-  if (!cogl_framebuffer_allocate (framebuffer, &error))
-    meta_fatal ("Failed to allocate onscreen framebuffer: %s\n",
-                error->message);
-
-  if (!(clutter_stage_window_parent_iface->realize (stage_window)))
-    meta_fatal ("Failed to realize native stage window");
-
-  return TRUE;
-}
-
 static void
 meta_stage_native_unrealize (ClutterStageWindow *stage_window)
 {
@@ -137,16 +202,6 @@ meta_stage_native_unrealize (ClutterStageWindow *stage_window)
 
   clutter_stage_window_parent_iface->unrealize (stage_window);
 
-  if (stage_native->frame_closure)
-    {
-      CoglOnscreen *onscreen;
-
-      onscreen = get_legacy_onscreen (stage_native);
-      cogl_onscreen_remove_frame_callback (onscreen,
-                                           stage_native->frame_closure);
-      stage_native->frame_closure = NULL;
-    }
-
   g_clear_pointer (&stage_native->pending_onscreen, cogl_object_unref);
 }
 
@@ -161,14 +216,18 @@ meta_stage_native_get_geometry (ClutterStageWindow    *stage_window,
                                 cairo_rectangle_int_t *geometry)
 {
   MetaBackend *backend = meta_get_backend ();
-  MetaRenderer *renderer = meta_backend_get_renderer (backend);
-  MetaRendererView *legacy_view;
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
 
-  legacy_view = get_legacy_view (renderer);
-  if (legacy_view)
+  if (monitor_manager)
     {
-      clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (legacy_view),
-                                     geometry);
+      int width, height;
+
+      meta_monitor_manager_get_screen_size (monitor_manager, &width, &height);
+      *geometry = (cairo_rectangle_int_t) {
+        .width = width,
+        .height = height,
+      };
     }
   else
     {
@@ -180,61 +239,25 @@ meta_stage_native_get_geometry (ClutterStageWindow    *stage_window,
 }
 
 static void
-frame_cb (CoglOnscreen  *onscreen,
-          CoglFrameEvent frame_event,
-          CoglFrameInfo *frame_info,
-          void          *user_data)
-{
-  MetaStageNative *stage_native = user_data;
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_native);
-  ClutterFrameInfo clutter_frame_info = {
-    .frame_counter = cogl_frame_info_get_frame_counter (frame_info),
-    .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
-    .presentation_time = cogl_frame_info_get_presentation_time (frame_info)
-  };
-
-  _clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info);
-}
-
-static void
 ensure_legacy_view (ClutterStageWindow *stage_window)
 {
   MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
   MetaBackend *backend = meta_get_backend ();
   MetaRenderer *renderer = meta_backend_get_renderer (backend);
-  MetaMonitorManager *monitor_manager =
-    meta_backend_get_monitor_manager (backend);
+  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
   MetaRendererView *legacy_view;
-  cairo_rectangle_int_t view_layout = { 0 };
-  CoglFramebuffer *framebuffer;
-  CoglOnscreen *onscreen;
 
   legacy_view = get_legacy_view (renderer);
   if (legacy_view)
     return;
 
-  if (!monitor_manager)
+  legacy_view = meta_renderer_native_create_legacy_view (renderer_native);
+  if (!legacy_view)
     return;
 
-  meta_monitor_manager_get_screen_size (monitor_manager,
-                                        &view_layout.width,
-                                        &view_layout.height);
-  framebuffer = g_steal_pointer (&stage_native->pending_onscreen);
-  legacy_view = g_object_new (META_TYPE_RENDERER_VIEW,
-                              "layout", &view_layout,
-                              "framebuffer", framebuffer,
-                              NULL);
   meta_renderer_set_legacy_view (renderer, legacy_view);
 
-  onscreen = COGL_ONSCREEN (framebuffer);
-  cogl_onscreen_set_swap_throttled (onscreen,
-                                    _clutter_get_sync_to_vblank ());
-
-  stage_native->frame_closure =
-    cogl_onscreen_add_frame_callback (onscreen,
-                                      frame_cb,
-                                      stage_native,
-                                      NULL);
+  ensure_frame_callback (stage_native, CLUTTER_STAGE_VIEW (legacy_view));
 }
 
 static GList *
@@ -243,29 +266,43 @@ meta_stage_native_get_views (ClutterStageWindow *stage_window)
   MetaBackend *backend = meta_get_backend ();
   MetaRenderer *renderer = meta_backend_get_renderer (backend);
 
-  ensure_legacy_view (stage_window);
+  if (!meta_is_stage_views_enabled ())
+    ensure_legacy_view (stage_window);
+
   return meta_renderer_get_views (renderer);
 }
 
 static int64_t
 meta_stage_native_get_frame_counter (ClutterStageWindow *stage_window)
 {
-  MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window);
-  CoglOnscreen *legacy_onscreen;
+  MetaBackend *backend = meta_get_backend ();
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
+  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
 
-  legacy_onscreen = get_legacy_onscreen (stage_native);
+  return meta_renderer_native_get_frame_counter (renderer_native);
+}
+
+static void
+meta_stage_native_finish_frame (ClutterStageWindow *stage_window)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
 
-  return cogl_onscreen_get_frame_counter (legacy_onscreen);
+  meta_renderer_native_finish_frame (META_RENDERER_NATIVE (renderer));
 }
 
 static void
 meta_stage_native_init (MetaStageNative *stage_native)
 {
+  stage_native->presented_frame_counter_sync = -1;
+  stage_native->presented_frame_counter_complete = -1;
 }
 
 static void
 meta_stage_native_class_init (MetaStageNativeClass *klass)
 {
+  quark_view_frame_closure =
+    g_quark_from_static_string ("-meta-native-stage-view-frame-closure");
 }
 
 static void
@@ -273,10 +310,10 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
 {
   clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
 
-  iface->realize = meta_stage_native_realize;
   iface->unrealize = meta_stage_native_unrealize;
   iface->can_clip_redraws = meta_stage_native_can_clip_redraws;
   iface->get_geometry = meta_stage_native_get_geometry;
   iface->get_views = meta_stage_native_get_views;
   iface->get_frame_counter = meta_stage_native_get_frame_counter;
+  iface->finish_frame = meta_stage_native_finish_frame;
 }
diff --git a/src/backends/native/meta-stage-native.h b/src/backends/native/meta-stage-native.h
index d0b87e0..d088609 100644
--- a/src/backends/native/meta-stage-native.h
+++ b/src/backends/native/meta-stage-native.h
@@ -31,6 +31,8 @@
 G_DECLARE_FINAL_TYPE (MetaStageNative, meta_stage_native,
                       META, STAGE_NATIVE, ClutterStageCogl)
 
+void meta_stage_native_rebuild_views (MetaStageNative *stage_native);
+
 void meta_stage_native_legacy_set_size (MetaStageNative *stage_native,
                                         int              width,
                                         int              height);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]