[mutter] renderer-native: Add hybrid GPU system support



commit c9259c2b153d4d8e2bbc2055e175dce87ebfd43a
Author: Jonas Ådahl <jadahl gmail com>
Date:   Tue Jul 25 10:21:04 2017 +0800

    renderer-native: Add hybrid GPU system support
    
    A hybrid GPU system is a system where more than one GPU is connected to
    connectors. A common configuration is having a integrated GPU (iGPU)
    connected to a laptop panel, and a dedicated GPU (dGPU) connected to
    one or more external connector (such as HDMI).
    
    This commit adds support for rendering the compositor stage using the
    iGPU, then copying the framebuffer content onto a secondary framebuffer
    that will be page flipped on the CRTC of the dGPU.
    
    This can work in two different ways: GPU accelerated using Open GL ES
    3, or CPU unaccelerated.
    
    When supported, GPU accelerated copying works by exporting the iGPU
    onscreen framebuffer as a DMA-BUF, importing it as a texture on a
    separate dGPU EGL context, then using glBlitFramebuffer(), blitting it
    onto a framebuffer on the dGPU that can then be page flipped on the dGPU
    CRTC.
    
    When GPU acceleration is not available, copying works by creating two
    dumb buffers, and each frame glReadPixels() from the iGPU EGL render
    context directly into the dumb buffer. The dumb buffer is then page
    flipped on the dGPU CRTC.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785381

 src/Makefile.am                                  |    2 +
 src/backends/native/meta-renderer-native-gles3.c |  169 ++++
 src/backends/native/meta-renderer-native-gles3.h |   47 +
 src/backends/native/meta-renderer-native.c       | 1169 ++++++++++++++++++----
 4 files changed, 1182 insertions(+), 205 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index c0d2a65..4f96a11 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -476,6 +476,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES +=             \
        backends/native/meta-output-kms.h               \
        backends/native/meta-renderer-native.c          \
        backends/native/meta-renderer-native.h          \
+       backends/native/meta-renderer-native-gles3.c    \
+       backends/native/meta-renderer-native-gles3.h    \
        backends/native/meta-stage-native.c             \
        backends/native/meta-stage-native.h             \
        backends/native/dbus-utils.c                    \
diff --git a/src/backends/native/meta-renderer-native-gles3.c 
b/src/backends/native/meta-renderer-native-gles3.c
new file mode 100644
index 0000000..daab1fc
--- /dev/null
+++ b/src/backends/native/meta-renderer-native-gles3.c
@@ -0,0 +1,169 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2017 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "backends/native/meta-renderer-native-gles3.h"
+
+#include <errno.h>
+#include <GLES3/gl3.h>
+
+#include "backends/meta-backend-private.h"
+#include "backends/meta-gles3.h"
+#include "backends/meta-gles3-table.h"
+#include "backends/native/meta-renderer-native.h"
+
+static EGLImageKHR
+create_egl_image (MetaEgl       *egl,
+                  EGLDisplay     egl_display,
+                  EGLContext     egl_context,
+                  unsigned int   width,
+                  unsigned int   height,
+                  uint32_t       stride,
+                  uint32_t       format,
+                  int            fd,
+                  GError       **error)
+{
+  EGLint attributes[] = {
+    EGL_WIDTH, width,
+    EGL_HEIGHT, height,
+    EGL_LINUX_DRM_FOURCC_EXT, format,
+    EGL_DMA_BUF_PLANE0_FD_EXT, fd,
+    EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
+    EGL_DMA_BUF_PLANE0_PITCH_EXT, stride,
+    EGL_NONE
+  };
+
+  return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
+                                EGL_LINUX_DMA_BUF_EXT, NULL,
+                                attributes,
+                                error);
+}
+
+static void
+paint_egl_image (MetaGles3   *gles3,
+                 EGLImageKHR  egl_image,
+                 int          width,
+                 int          height)
+{
+  GLuint texture;
+  GLuint framebuffer;
+
+  meta_gles3_clear_error (gles3);
+
+  GLBAS (gles3, glGenFramebuffers, (1, &framebuffer));
+  GLBAS (gles3, glBindFramebuffer, (GL_READ_FRAMEBUFFER, framebuffer));
+
+  GLBAS (gles3, glActiveTexture, (GL_TEXTURE0));
+  GLBAS (gles3, glGenTextures, (1, &texture));
+  GLBAS (gles3, glBindTexture, (GL_TEXTURE_2D, texture));
+  GLEXT (gles3, glEGLImageTargetTexture2DOES, (GL_TEXTURE_2D, egl_image));
+  GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+                                  GL_NEAREST));
+  GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                                  GL_NEAREST));
+  GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                                  GL_CLAMP_TO_EDGE));
+  GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                                  GL_CLAMP_TO_EDGE));
+  GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_WRAP_R_OES,
+                                  GL_CLAMP_TO_EDGE));
+
+  GLBAS (gles3, glFramebufferTexture2D, (GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                         GL_TEXTURE_2D, texture, 0));
+
+  GLBAS (gles3, glBindFramebuffer, (GL_READ_FRAMEBUFFER, framebuffer));
+  GLBAS (gles3, glBlitFramebuffer, (0, height, width, 0,
+                                    0, 0, width, height,
+                                    GL_COLOR_BUFFER_BIT,
+                                    GL_NEAREST));
+}
+
+gboolean
+meta_renderer_native_gles3_blit_shared_bo (MetaEgl        *egl,
+                                           MetaGles3      *gles3,
+                                           MetaGpuKms     *gpu_kms,
+                                           EGLDisplay      egl_display,
+                                           EGLContext      egl_context,
+                                           EGLSurface      egl_surface,
+                                           struct gbm_bo  *shared_bo,
+                                           GError        **error)
+{
+  int shared_bo_fd;
+  unsigned int width;
+  unsigned int height;
+  uint32_t stride;
+  uint32_t format;
+  EGLImageKHR egl_image;
+
+  shared_bo_fd = gbm_bo_get_fd (shared_bo);
+  if (shared_bo_fd < 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to export gbm_bo: %s", strerror (errno));
+      return FALSE;
+    }
+
+  width = gbm_bo_get_width (shared_bo);
+  height = gbm_bo_get_height (shared_bo);
+  stride = gbm_bo_get_stride (shared_bo);
+  format = gbm_bo_get_format (shared_bo);
+
+  egl_image = create_egl_image (egl,
+                                egl_display,
+                                egl_context,
+                                width, height, stride,
+                                format,
+                                shared_bo_fd,
+                                error);
+  close (shared_bo_fd);
+
+  if (!egl_image)
+    return FALSE;
+
+  paint_egl_image (gles3, egl_image, width, height);
+
+  meta_egl_destroy_image (egl, egl_display, egl_image, NULL);
+
+  return TRUE;
+}
+
+void
+meta_renderer_native_gles3_read_pixels (MetaEgl   *egl,
+                                        MetaGles3 *gles3,
+                                        int        width,
+                                        int        height,
+                                        uint8_t   *target_data)
+{
+  int y;
+
+  GLBAS (gles3, glFinish, ());
+
+  for (y = 0; y < height; y++)
+    {
+      GLBAS (gles3, glReadPixels, (0, height - y, width, 1,
+                                   GL_RGBA, GL_UNSIGNED_BYTE,
+                                   target_data + width * y * 4));
+    }
+}
diff --git a/src/backends/native/meta-renderer-native-gles3.h 
b/src/backends/native/meta-renderer-native-gles3.h
new file mode 100644
index 0000000..5189363
--- /dev/null
+++ b/src/backends/native/meta-renderer-native-gles3.h
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2017 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#ifndef META_RENDERER_NATIVE_GLES3_H
+#define META_RENDERER_NATIVE_GLES3_H
+
+#include <gbm.h>
+
+#include "backends/meta-egl.h"
+#include "backends/meta-gles3.h"
+#include "backends/native/meta-gpu-kms.h"
+
+gboolean meta_renderer_native_gles3_blit_shared_bo (MetaEgl       *egl,
+                                                    MetaGles3     *gles3,
+                                                    MetaGpuKms    *gpu_kms,
+                                                    EGLDisplay     egl_display,
+                                                    EGLContext     egl_context,
+                                                    EGLSurface     egl_surface,
+                                                    struct gbm_bo *shared_bo,
+                                                    GError       **error);
+
+void meta_renderer_native_gles3_read_pixels (MetaEgl   *egl,
+                                             MetaGles3 *gles3,
+                                             int        width,
+                                             int        height,
+                                             uint8_t   *target_data);
+
+#endif /* META_RENDERER_NATIVE_GLES3_H */
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index d3106e7..fb34e35 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -49,6 +49,7 @@
 
 #include "backends/meta-backend-private.h"
 #include "backends/meta-crtc.h"
+#include "backends/meta-gles3.h"
 #include "backends/meta-egl.h"
 #include "backends/meta-egl-ext.h"
 #include "backends/meta-logical-monitor.h"
@@ -57,6 +58,7 @@
 #include "backends/native/meta-gpu-kms.h"
 #include "backends/native/meta-monitor-manager-kms.h"
 #include "backends/native/meta-renderer-native.h"
+#include "backends/native/meta-renderer-native-gles3.h"
 #include "cogl/cogl.h"
 #include "core/boxes-private.h"
 
@@ -75,6 +77,12 @@ enum
 
 static GParamSpec *obj_props[PROP_LAST];
 
+typedef enum _MetaSharedFramebufferCopyMode
+{
+  META_SHARED_FRAMEBUFFER_COPY_MODE_GPU,
+  META_SHARED_FRAMEBUFFER_COPY_MODE_CPU
+} MetaSharedFramebufferCopyMode;
+
 typedef struct _MetaRendererNativeGpuData
 {
   MetaRendererNative *renderer_native;
@@ -94,6 +102,19 @@ typedef struct _MetaRendererNativeGpuData
   MetaRendererNativeMode mode;
 
   gboolean no_add_fb2;
+
+  EGLDisplay egl_display;
+
+  /*
+   * Fields used for blitting iGPU framebuffer content onto dGPU framebuffers.
+   */
+  struct {
+    MetaSharedFramebufferCopyMode copy_mode;
+
+    /* For GPU blit mode */
+    EGLContext egl_context;
+    EGLConfig egl_config;
+  } secondary;
 } MetaRendererNativeGpuData;
 
 typedef struct _MetaDumbBuffer
@@ -104,9 +125,36 @@ typedef struct _MetaDumbBuffer
   uint64_t map_size;
 } MetaDumbBuffer;
 
-typedef struct _MetaOnscreenNative
+typedef struct _MetaOnscreenNativeSecondaryGpuState
 {
   MetaGpuKms *gpu_kms;
+  MetaRendererNativeGpuData *renderer_gpu_data;
+
+  EGLSurface egl_surface;
+
+  struct {
+    struct gbm_surface *surface;
+    uint32_t current_fb_id;
+    uint32_t next_fb_id;
+    struct gbm_bo *current_bo;
+    struct gbm_bo *next_bo;
+  } gbm;
+
+  struct {
+    MetaDumbBuffer *dumb_fb;
+    MetaDumbBuffer dumb_fbs[2];
+  } cpu;
+
+  int pending_flips;
+} MetaOnscreenNativeSecondaryGpuState;
+
+typedef struct _MetaOnscreenNative
+{
+  MetaRendererNative *renderer_native;
+  MetaGpuKms *render_gpu;
+  MetaLogicalMonitor *logical_monitor;
+
+  GHashTable *secondary_gpu_states;
 
   struct {
     struct gbm_surface *surface;
@@ -133,7 +181,7 @@ typedef struct _MetaOnscreenNative
   int64_t pending_swap_notify_frame_count;
 
   MetaRendererView *view;
-  int pending_flips;
+  int total_pending_flips;
 } MetaOnscreenNative;
 
 struct _MetaRendererNative
@@ -141,8 +189,7 @@ struct _MetaRendererNative
   MetaRenderer parent;
 
   MetaMonitorManagerKms *monitor_manager_kms;
-
-  EGLDisplay egl_display;
+  MetaGles3 *gles3;
 
   GHashTable *gpu_datas;
 
@@ -176,9 +223,26 @@ init_dumb_fb (MetaDumbBuffer *dumb_fb,
               uint32_t        format,
               GError        **error);
 
+static MetaEgl *
+meta_renderer_native_get_egl (MetaRendererNative *renderer_native);
+
+static void
+free_current_secondary_bo (MetaGpuKms                          *gpu_kms,
+                           MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state);
+
+static void
+free_next_secondary_bo (MetaGpuKms                          *gpu_kms,
+                        MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state);
+
 static void
 meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data)
 {
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+
+  if (renderer_gpu_data->egl_display != EGL_NO_DISPLAY)
+    meta_egl_terminate (egl, renderer_gpu_data->egl_display, NULL);
+
   g_free (renderer_gpu_data);
 }
 
@@ -190,16 +254,6 @@ meta_renderer_native_get_gpu_data (MetaRendererNative *renderer_native,
 }
 
 static MetaRendererNative *
-meta_renderer_native_from_onscreen (CoglOnscreen *onscreen)
-{
-  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-  CoglRenderer *cogl_renderer = framebuffer->context->display->renderer;
-  CoglRendererEGL *egl_renderer = cogl_renderer->winsys;
-
-  return egl_renderer->platform;
-}
-
-static MetaRendererNative *
 meta_renderer_native_from_gpu (MetaGpuKms *gpu_kms)
 {
   MetaMonitorManager *monitor_manager =
@@ -212,8 +266,7 @@ meta_renderer_native_from_gpu (MetaGpuKms *gpu_kms)
 struct gbm_device *
 meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms)
 {
-  MetaRendererNative *renderer_native =
-    meta_renderer_native_from_gpu (gpu_kms);
+  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
   MetaRendererNativeGpuData *renderer_gpu_data;
 
   renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
@@ -228,6 +281,16 @@ meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms)
   return g_new0 (MetaRendererNativeGpuData, 1);
 }
 
+static MetaOnscreenNativeSecondaryGpuState *
+get_secondary_gpu_state (CoglOnscreen *onscreen,
+                         MetaGpuKms   *gpu_kms)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+
+  return g_hash_table_lookup (onscreen_native->secondary_gpu_states, gpu_kms);
+}
+
 static MetaEgl *
 meta_renderer_native_get_egl (MetaRendererNative *renderer_native)
 {
@@ -238,15 +301,185 @@ meta_renderer_native_get_egl (MetaRendererNative *renderer_native)
   return meta_backend_get_egl (backend);
 }
 
+static MetaEgl *
+meta_onscreen_native_get_egl (MetaOnscreenNative *onscreen_native)
+{
+  return meta_renderer_native_get_egl (onscreen_native->renderer_native);
+}
+
+static gboolean
+init_secondary_gpu_state_gpu_copy_mode (MetaRendererNative         *renderer_native,
+                                        CoglOnscreen               *onscreen,
+                                        MetaRendererNativeGpuData  *renderer_gpu_data,
+                                        MetaGpuKms                 *gpu_kms,
+                                        GError                    **error)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
+  int width, height;
+  EGLNativeWindowType egl_native_window;
+  struct gbm_surface *gbm_surface;
+  EGLSurface egl_surface;
+  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+
+  gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device,
+                                    width, height,
+                                    GBM_FORMAT_XRGB8888,
+                                    GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+  if (!gbm_surface)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to create gbm_surface: %s", strerror (errno));
+      return FALSE;
+    }
+
+  egl_native_window = (EGLNativeWindowType) gbm_surface;
+  egl_surface =
+    meta_egl_create_window_surface (egl,
+                                    renderer_gpu_data->egl_display,
+                                    renderer_gpu_data->secondary.egl_config,
+                                    egl_native_window,
+                                    NULL,
+                                    error);
+  if (egl_surface == EGL_NO_SURFACE)
+    {
+      gbm_surface_destroy (gbm_surface);
+      return FALSE;
+    }
+
+  secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1);
+
+  secondary_gpu_state->gpu_kms = gpu_kms;
+  secondary_gpu_state->renderer_gpu_data = renderer_gpu_data;
+  secondary_gpu_state->gbm.surface = gbm_surface;
+  secondary_gpu_state->egl_surface = egl_surface;
+
+  g_hash_table_insert (onscreen_native->secondary_gpu_states,
+                       gpu_kms, secondary_gpu_state);
+
+  return TRUE;
+}
+
+static void
+secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaEgl *egl = meta_backend_get_egl (backend);
+  MetaGpuKms *gpu_kms = secondary_gpu_state->gpu_kms;
+  unsigned int i;
+
+  if (secondary_gpu_state->egl_surface != EGL_NO_SURFACE)
+    {
+      MetaRendererNativeGpuData *renderer_gpu_data;
+
+      renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
+      meta_egl_destroy_surface (egl,
+                                renderer_gpu_data->egl_display,
+                                secondary_gpu_state->egl_surface,
+                                NULL);
+    }
+
+  free_current_secondary_bo (gpu_kms, secondary_gpu_state);
+  free_next_secondary_bo (gpu_kms, secondary_gpu_state);
+  g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy);
+
+  for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
+    {
+      MetaDumbBuffer *dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[i];
+
+      if (dumb_fb->fb_id)
+        release_dumb_fb (dumb_fb, gpu_kms);
+    }
+
+  g_free (secondary_gpu_state);
+}
+
+static gboolean
+init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative         *renderer_native,
+                                        CoglOnscreen               *onscreen,
+                                        MetaRendererNativeGpuData  *renderer_gpu_data,
+                                        MetaGpuKms                 *gpu_kms,
+                                        GError                    **error)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+  int width, height;
+  unsigned int i;
+
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+
+  secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1);
+  secondary_gpu_state->renderer_gpu_data = renderer_gpu_data;
+  secondary_gpu_state->gpu_kms = gpu_kms;
+  secondary_gpu_state->egl_surface = EGL_NO_SURFACE;
+
+  for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++)
+    {
+      MetaDumbBuffer *dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[i];
+
+      if (!init_dumb_fb (dumb_fb,
+                         gpu_kms,
+                         width, height,
+                         GBM_FORMAT_XBGR8888,
+                         error))
+        {
+          secondary_gpu_state_free (secondary_gpu_state);
+          return FALSE;
+        }
+    }
+
+  g_hash_table_insert (onscreen_native->secondary_gpu_states,
+                       gpu_kms, secondary_gpu_state);
+
+  return TRUE;
+}
+
+static gboolean
+init_secondary_gpu_state (MetaRendererNative  *renderer_native,
+                          CoglOnscreen        *onscreen,
+                          MetaGpuKms          *gpu_kms,
+                          GError             **error)
+{
+  MetaRendererNativeGpuData *renderer_gpu_data;
+
+  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
+                                                         gpu_kms);
+
+  switch (renderer_gpu_data->secondary.copy_mode)
+    {
+    case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
+      if (!init_secondary_gpu_state_gpu_copy_mode (renderer_native,
+                                                   onscreen,
+                                                   renderer_gpu_data,
+                                                   gpu_kms,
+                                                   error))
+        return FALSE;
+      break;
+    case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
+      if (!init_secondary_gpu_state_cpu_copy_mode (renderer_native,
+                                                   onscreen,
+                                                   renderer_gpu_data,
+                                                   gpu_kms,
+                                                   error))
+        return FALSE;
+      break;
+    }
+
+  return TRUE;
+}
+
 static void
 meta_renderer_native_disconnect (CoglRenderer *cogl_renderer)
 {
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
-  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
-
-  if (cogl_renderer_egl->edpy != EGL_NO_DISPLAY)
-    meta_egl_terminate (egl, cogl_renderer_egl->edpy, NULL);
 
   g_slice_free (CoglRendererEGL, cogl_renderer_egl);
 }
@@ -284,7 +517,8 @@ flush_pending_swap_notify_idle (void *user_data)
 {
   CoglContext *cogl_context = user_data;
   CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
   GList *l;
 
   /* This needs to be disconnected before invoking the callbacks in
@@ -305,14 +539,44 @@ flush_pending_swap_notify_idle (void *user_data)
 }
 
 static void
+free_current_secondary_bo (MetaGpuKms                          *gpu_kms,
+                           MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
+{
+  MetaRendererNativeGpuData *renderer_gpu_data;
+  int kms_fd;
+
+  kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+
+  renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
+  switch (renderer_gpu_data->secondary.copy_mode)
+    {
+    case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
+      if (secondary_gpu_state->gbm.current_fb_id)
+        {
+          drmModeRmFB (kms_fd, secondary_gpu_state->gbm.current_fb_id);
+          secondary_gpu_state->gbm.current_fb_id = 0;
+        }
+      if (secondary_gpu_state->gbm.current_bo)
+        {
+          gbm_surface_release_buffer (secondary_gpu_state->gbm.surface,
+                                      secondary_gpu_state->gbm.current_bo);
+          secondary_gpu_state->gbm.current_bo = NULL;
+        }
+      break;
+    case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
+      break;
+    }
+}
+
+static void
 free_current_bo (CoglOnscreen *onscreen)
 {
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   int kms_fd;
 
-  kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+  kms_fd = meta_gpu_kms_get_fd (render_gpu);
 
   if (onscreen_native->gbm.current_fb_id)
     {
@@ -325,6 +589,10 @@ free_current_bo (CoglOnscreen *onscreen)
                                   onscreen_native->gbm.current_bo);
       onscreen_native->gbm.current_bo = NULL;
     }
+
+  g_hash_table_foreach (onscreen_native->secondary_gpu_states,
+                        (GHFunc) free_current_secondary_bo,
+                        NULL);
 }
 
 static void
@@ -332,13 +600,7 @@ meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
 {
   CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  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 *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
 
   onscreen_native->pending_swap_notify_frame_count =
     onscreen_native->pending_queue_swap_notify_frame_count;
@@ -351,6 +613,10 @@ meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen)
    * immediately notifying we queue an idle callback */
   if (!renderer_native->swap_notify_idle)
     {
+      CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+      CoglContext *cogl_context = framebuffer->context;
+      CoglRenderer *cogl_renderer = cogl_context->display->renderer;
+
       renderer_native->swap_notify_idle =
         _cogl_poll_renderer_add_idle (cogl_renderer,
                                       flush_pending_swap_notify_idle,
@@ -372,17 +638,20 @@ static gboolean
 meta_renderer_native_connect (CoglRenderer *cogl_renderer,
                               GError      **error)
 {
-  MetaBackend *backend = meta_get_backend ();
-  MetaRenderer *renderer = meta_backend_get_renderer (backend);
-  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
   CoglRendererEGL *cogl_renderer_egl;
+  MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data;
+  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
+  MetaRendererNativeGpuData *renderer_gpu_data;
 
   cogl_renderer->winsys = g_slice_new0 (CoglRendererEGL);
   cogl_renderer_egl = cogl_renderer->winsys;
 
+  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
+                                                         gpu_kms);
+
   cogl_renderer_egl->platform_vtable = &_cogl_winsys_egl_vtable;
-  cogl_renderer_egl->platform = renderer_native;
-  cogl_renderer_egl->edpy = renderer_native->egl_display;
+  cogl_renderer_egl->platform = renderer_gpu_data;
+  cogl_renderer_egl->edpy = renderer_gpu_data->egl_display;
 
   if (!_cogl_winsys_egl_renderer_connect_common (cogl_renderer, error))
     goto fail;
@@ -401,16 +670,10 @@ meta_renderer_native_add_egl_config_attributes (CoglDisplay           *cogl_disp
                                                 EGLint                *attributes)
 {
   CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
-  MetaMonitorManagerKms *monitor_manager_kms =
-    renderer_native->monitor_manager_kms;
-  MetaGpuKms *primary_gpu =
-    meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
-  MetaRendererNativeGpuData *primary_renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (renderer_native, primary_gpu);
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
   int i = 0;
 
-  switch (primary_renderer_gpu_data->mode)
+  switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
       attributes[i++] = EGL_SURFACE_TYPE;
@@ -433,7 +696,8 @@ meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display,
 {
   CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
   CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
 
   cogl_display_egl->platform = renderer_native;
 
@@ -518,7 +782,8 @@ meta_renderer_native_egl_cleanup_context (CoglDisplay *cogl_display)
   CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
   CoglRenderer *cogl_renderer = cogl_display->renderer;
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
   MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
 
   if (cogl_display_egl->dummy_surface != EGL_NO_SURFACE)
@@ -532,6 +797,17 @@ meta_renderer_native_egl_cleanup_context (CoglDisplay *cogl_display)
 }
 
 static void
+swap_secondary_drm_fb (MetaGpuKms                          *gpu_kms,
+                       MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
+{
+  secondary_gpu_state->gbm.current_fb_id = secondary_gpu_state->gbm.next_fb_id;
+  secondary_gpu_state->gbm.next_fb_id = 0;
+
+  secondary_gpu_state->gbm.current_bo = secondary_gpu_state->gbm.next_bo;
+  secondary_gpu_state->gbm.next_bo = NULL;
+}
+
+static void
 meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
 {
   CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
@@ -544,6 +820,10 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen)
 
   onscreen_native->gbm.current_bo = onscreen_native->gbm.next_bo;
   onscreen_native->gbm.next_bo = NULL;
+
+  g_hash_table_foreach (onscreen_native->secondary_gpu_states,
+                        (GHFunc) swap_secondary_drm_fb,
+                        NULL);
 }
 
 static void
@@ -557,12 +837,20 @@ on_crtc_flipped (GClosure         *closure,
   CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
   CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
+
+  if (gpu_kms != render_gpu)
+    {
+      MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+
+      secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
+      secondary_gpu_state->pending_flips--;
+    }
 
-  onscreen_native->pending_flips--;
-  if (onscreen_native->pending_flips == 0)
+  onscreen_native->total_pending_flips--;
+  if (onscreen_native->total_pending_flips == 0)
     {
-      MetaRendererNative *renderer_native =
-        meta_renderer_native_from_onscreen (onscreen);
       MetaRendererNativeGpuData *renderer_gpu_data;
 
       onscreen_native->pending_queue_swap_notify = FALSE;
@@ -571,7 +859,7 @@ on_crtc_flipped (GClosure         *closure,
 
       renderer_gpu_data =
         meta_renderer_native_get_gpu_data (renderer_native,
-                                           onscreen_native->gpu_kms);
+                                           onscreen_native->render_gpu);
       switch (renderer_gpu_data->mode)
         {
         case META_RENDERER_NATIVE_MODE_GBM:
@@ -586,6 +874,33 @@ on_crtc_flipped (GClosure         *closure,
 }
 
 static void
+free_next_secondary_bo (MetaGpuKms                          *gpu_kms,
+                        MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state)
+{
+  MetaRendererNativeGpuData *renderer_gpu_data;
+
+  renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
+  switch (renderer_gpu_data->secondary.copy_mode)
+    {
+    case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
+      if (secondary_gpu_state->gbm.next_fb_id)
+        {
+          int kms_fd;
+
+          kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+          drmModeRmFB (kms_fd, secondary_gpu_state->gbm.next_fb_id);
+          gbm_surface_release_buffer (secondary_gpu_state->gbm.surface,
+                                      secondary_gpu_state->gbm.next_bo);
+          secondary_gpu_state->gbm.next_fb_id = 0;
+          secondary_gpu_state->gbm.next_bo = NULL;
+        }
+      break;
+    case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
+      break;
+    }
+}
+
+static void
 flip_closure_destroyed (MetaRendererView *view)
 {
   ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view);
@@ -594,13 +909,12 @@ flip_closure_destroyed (MetaRendererView *view)
   CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
   CoglOnscreenEGL *onscreen_egl =  onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
-  MetaRendererNative *renderer_native =
-    meta_renderer_native_from_onscreen (onscreen);
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   MetaRendererNativeGpuData *renderer_gpu_data;
 
-  renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (renderer_native, gpu_kms);
+  renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
+                                                         render_gpu);
   switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
@@ -608,7 +922,7 @@ flip_closure_destroyed (MetaRendererView *view)
         {
           int kms_fd;
 
-          kms_fd = meta_gpu_kms_get_fd (gpu_kms);
+          kms_fd = meta_gpu_kms_get_fd (render_gpu);
           drmModeRmFB (kms_fd, onscreen_native->gbm.next_fb_id);
           gbm_surface_release_buffer (onscreen_native->gbm.surface,
                                       onscreen_native->gbm.next_bo);
@@ -616,6 +930,10 @@ flip_closure_destroyed (MetaRendererView *view)
           onscreen_native->gbm.next_fb_id = 0;
         }
 
+      g_hash_table_foreach (onscreen_native->secondary_gpu_states,
+                            (GHFunc) free_next_secondary_bo,
+                            NULL);
+
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
@@ -637,22 +955,15 @@ static gboolean
 flip_egl_stream (MetaOnscreenNative *onscreen_native,
                  GClosure           *flip_closure)
 {
-  MetaRendererNative *renderer_native =
-    meta_renderer_native_from_gpu (onscreen_native->gpu_kms);
   MetaRendererNativeGpuData *renderer_gpu_data;
-  MetaBackend *backend = meta_get_backend ();
-  MetaEgl *egl = meta_backend_get_egl (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;
-  CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
+  EGLDisplay *egl_display;
+  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
   EGLAttrib *acquire_attribs;
   GError *error = NULL;
 
   renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (renderer_native,
-                                       onscreen_native->gpu_kms);
+    meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
+                                       onscreen_native->render_gpu);
   if (renderer_gpu_data->egl.no_egl_output_drm_flip_event)
     return FALSE;
 
@@ -662,8 +973,9 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
     EGL_NONE
   };
 
+  egl_display = renderer_gpu_data->egl_display;
   if (!meta_egl_stream_consumer_acquire_attrib (egl,
-                                                cogl_renderer_egl->edpy,
+                                                egl_display,
                                                 onscreen_native->egl.stream,
                                                 acquire_attribs,
                                                 &error))
@@ -686,17 +998,23 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native,
 #endif /* HAVE_EGL_DEVICE */
 
 static void
-meta_onscreen_native_flip_crtc (MetaOnscreenNative *onscreen_native,
-                                GClosure           *flip_closure,
-                                MetaCrtc           *crtc,
-                                int                 x,
-                                int                 y,
-                                gboolean           *fb_in_use)
-{
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
-  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
+meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
+                                GClosure     *flip_closure,
+                                MetaCrtc     *crtc,
+                                int           x,
+                                int           y,
+                                gboolean     *fb_in_use)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   MetaRendererNativeGpuData *renderer_gpu_data;
+  MetaGpuKms *gpu_kms;
+  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL;
+  uint32_t fb_id;
 
+  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
   if (!meta_gpu_kms_is_crtc_active (gpu_kms, crtc))
     {
       *fb_in_use = FALSE;
@@ -704,23 +1022,38 @@ meta_onscreen_native_flip_crtc (MetaOnscreenNative *onscreen_native,
     }
 
   renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                         gpu_kms);
+                                                         render_gpu);
   switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
-      if (meta_gpu_kms_flip_crtc (gpu_kms,
-                                  crtc,
-                                  x, y,
-                                  onscreen_native->gbm.next_fb_id,
-                                  flip_closure,
-                                  fb_in_use))
-        onscreen_native->pending_flips++;
+      if (gpu_kms == render_gpu)
+        {
+          fb_id = onscreen_native->gbm.next_fb_id;
+        }
+      else
+        {
+          secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
+          fb_id = secondary_gpu_state->gbm.next_fb_id;
+        }
+
+      if (!meta_gpu_kms_flip_crtc (gpu_kms,
+                                   crtc,
+                                   x, y,
+                                   fb_id,
+                                   flip_closure,
+                                   fb_in_use))
+        return;
+
+      onscreen_native->total_pending_flips++;
+      if (secondary_gpu_state)
+        secondary_gpu_state->pending_flips++;
+
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       if (flip_egl_stream (onscreen_native,
                            flip_closure))
-        onscreen_native->pending_flips++;
+        onscreen_native->total_pending_flips++;
       *fb_in_use = TRUE;
       break;
 #endif
@@ -729,8 +1062,8 @@ meta_onscreen_native_flip_crtc (MetaOnscreenNative *onscreen_native,
 
 typedef struct _SetCrtcFbData
 {
-  MetaGpuKms *gpu_kms;
-  MetaLogicalMonitor *logical_monitor;
+  MetaGpuKms *render_gpu;
+  CoglOnscreen *onscreen;
   uint32_t fb_id;
 } SetCrtcFbData;
 
@@ -740,27 +1073,48 @@ set_crtc_fb (MetaLogicalMonitor *logical_monitor,
              gpointer            user_data)
 {
   SetCrtcFbData *data = user_data;
-  MetaGpuKms *gpu_kms = data->gpu_kms;
+  MetaGpuKms *render_gpu = data->render_gpu;
+  CoglOnscreen *onscreen = data->onscreen;
+  MetaGpuKms *gpu_kms;
+  uint32_t fb_id;
   int x, y;
 
+  gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
+  if (gpu_kms == render_gpu)
+    {
+      fb_id = data->fb_id;
+    }
+  else
+    {
+      MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+
+      secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms);
+      if (!secondary_gpu_state)
+        return;
+
+      fb_id = secondary_gpu_state->gbm.next_fb_id;
+    }
+
   x = crtc->rect.x - logical_monitor->rect.x;
   y = crtc->rect.y - logical_monitor->rect.y;
 
-  meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, x, y, data->fb_id);
+  meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, x, y, fb_id);
 }
 
 static void
-meta_onscreen_native_set_crtc_modes (MetaOnscreenNative *onscreen_native)
+meta_onscreen_native_set_crtc_modes (CoglOnscreen *onscreen)
 {
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
-  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   MetaRendererNativeGpuData *renderer_gpu_data;
   MetaRendererView *view = onscreen_native->view;
   uint32_t fb_id = 0;
   MetaLogicalMonitor *logical_monitor;
 
   renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                         gpu_kms);
+                                                         render_gpu);
   switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
@@ -779,7 +1133,8 @@ meta_onscreen_native_set_crtc_modes (MetaOnscreenNative *onscreen_native)
   if (logical_monitor)
     {
       SetCrtcFbData data = {
-        .gpu_kms = gpu_kms,
+        .render_gpu = render_gpu,
+        .onscreen = onscreen,
         .fb_id = fb_id
       };
 
@@ -791,11 +1146,11 @@ meta_onscreen_native_set_crtc_modes (MetaOnscreenNative *onscreen_native)
     {
       GList *l;
 
-      for (l = meta_gpu_get_crtcs (META_GPU (gpu_kms)); l; l = l->next)
+      for (l = meta_gpu_get_crtcs (META_GPU (render_gpu)); l; l = l->next)
         {
           MetaCrtc *crtc = l->data;
 
-          meta_gpu_kms_apply_crtc_mode (gpu_kms,
+          meta_gpu_kms_apply_crtc_mode (render_gpu,
                                         crtc,
                                         crtc->rect.x, crtc->rect.y,
                                         fb_id);
@@ -805,7 +1160,7 @@ meta_onscreen_native_set_crtc_modes (MetaOnscreenNative *onscreen_native)
 
 typedef struct _FlipCrtcData
 {
-  MetaOnscreenNative *onscreen_native;
+  CoglOnscreen *onscreen;
   GClosure *flip_closure;
 
   gboolean out_fb_in_use;
@@ -822,7 +1177,7 @@ flip_crtc (MetaLogicalMonitor *logical_monitor,
   x = crtc->rect.x - logical_monitor->rect.x;
   y = crtc->rect.y - logical_monitor->rect.y;
 
-  meta_onscreen_native_flip_crtc (data->onscreen_native,
+  meta_onscreen_native_flip_crtc (data->onscreen,
                                   data->flip_closure,
                                   crtc, x, y,
                                   &data->out_fb_in_use);
@@ -833,8 +1188,7 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
 {
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
-  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   MetaRendererView *view = onscreen_native->view;
   GClosure *flip_closure;
   MetaLogicalMonitor *logical_monitor;
@@ -862,7 +1216,7 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
   if (logical_monitor)
     {
       FlipCrtcData data = {
-        .onscreen_native = onscreen_native,
+        .onscreen = onscreen,
         .flip_closure = flip_closure,
       };
 
@@ -875,11 +1229,11 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
     {
       GList *l;
 
-      for (l = meta_gpu_get_crtcs (META_GPU (gpu_kms)); l; l = l->next)
+      for (l = meta_gpu_get_crtcs (META_GPU (render_gpu)); l; l = l->next)
         {
           MetaCrtc *crtc = l->data;
 
-          meta_onscreen_native_flip_crtc (onscreen_native, flip_closure,
+          meta_onscreen_native_flip_crtc (onscreen, flip_closure,
                                           crtc, crtc->rect.x, crtc->rect.y,
                                           &fb_in_use);
         }
@@ -891,12 +1245,13 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
    * Since we won't receive a flip callback, lets just notify listeners
    * directly.
    */
-  if (fb_in_use && onscreen_native->pending_flips == 0)
+  if (fb_in_use && onscreen_native->total_pending_flips == 0)
     {
+      MetaRendererNative *renderer_native = onscreen_native->renderer_native;
       MetaRendererNativeGpuData *renderer_gpu_data;
 
       renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                             gpu_kms);
+                                                             render_gpu);
       switch (renderer_gpu_data->mode)
         {
         case META_RENDERER_NATIVE_MODE_GBM:
@@ -915,20 +1270,18 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen)
 }
 
 static gboolean
-gbm_get_next_fb_id (CoglOnscreen   *onscreen,
-                    struct gbm_bo **out_next_bo,
-                    uint32_t       *out_next_fb_id)
+gbm_get_next_fb_id (MetaGpuKms         *gpu_kms,
+                    struct gbm_surface *gbm_surface,
+                    struct gbm_bo     **out_next_bo,
+                    uint32_t           *out_next_fb_id)
 {
-  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
-  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
   uint32_t handle, stride;
   struct gbm_bo *next_bo;
   uint32_t next_fb_id;
   int kms_fd;
 
   /* Now we need to set the CRTC to whatever is the front buffer */
-  next_bo = gbm_surface_lock_front_buffer (onscreen_native->gbm.surface);
+  next_bo = gbm_surface_lock_front_buffer (gbm_surface);
 
   stride = gbm_bo_get_stride (next_bo);
   handle = gbm_bo_get_handle (next_bo).u32;
@@ -936,8 +1289,8 @@ gbm_get_next_fb_id (CoglOnscreen   *onscreen,
   kms_fd = meta_gpu_kms_get_fd (gpu_kms);
 
   if (drmModeAddFB (kms_fd,
-                    cogl_framebuffer_get_width (COGL_FRAMEBUFFER (onscreen)),
-                    cogl_framebuffer_get_height (COGL_FRAMEBUFFER (onscreen)),
+                    gbm_bo_get_width (next_bo),
+                    gbm_bo_get_height (next_bo),
                     24, /* depth */
                     32, /* bpp */
                     stride,
@@ -945,7 +1298,7 @@ gbm_get_next_fb_id (CoglOnscreen   *onscreen,
                     &next_fb_id))
     {
       g_warning ("Failed to create new back buffer handle: %m");
-      gbm_surface_release_buffer (onscreen_native->gbm.surface, next_bo);
+      gbm_surface_release_buffer (gbm_surface, next_bo);
       return FALSE;
     }
 
@@ -955,19 +1308,200 @@ gbm_get_next_fb_id (CoglOnscreen   *onscreen,
 }
 
 static void
+wait_for_pending_flips (CoglOnscreen *onscreen)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+  GHashTableIter iter;
+
+  g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
+  while (g_hash_table_iter_next (&iter,
+                                 NULL,
+                                 (gpointer *) &secondary_gpu_state))
+    {
+      while (secondary_gpu_state->pending_flips)
+        meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, NULL);
+    }
+
+  while (onscreen_native->total_pending_flips)
+    meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, NULL);
+}
+
+static void
+copy_shared_framebuffer_gpu (CoglOnscreen                        *onscreen,
+                             MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
+                             MetaRendererNativeGpuData           *renderer_gpu_data,
+                             gboolean                            *egl_context_changed)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+  GError *error = NULL;
+
+  if (!meta_egl_make_current (egl,
+                              renderer_gpu_data->egl_display,
+                              secondary_gpu_state->egl_surface,
+                              secondary_gpu_state->egl_surface,
+                              renderer_gpu_data->secondary.egl_context,
+                              &error))
+    {
+      g_warning ("Failed to make current: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  *egl_context_changed = TRUE;
+
+  if (!meta_renderer_native_gles3_blit_shared_bo (egl,
+                                                  renderer_native->gles3,
+                                                  secondary_gpu_state->gpu_kms,
+                                                  renderer_gpu_data->egl_display,
+                                                  renderer_gpu_data->secondary.egl_context,
+                                                  secondary_gpu_state->egl_surface,
+                                                  onscreen_native->gbm.next_bo,
+                                                  &error))
+    {
+      g_warning ("Failed to blit shared framebuffer: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  if (!meta_egl_swap_buffers (egl,
+                              renderer_gpu_data->egl_display,
+                              secondary_gpu_state->egl_surface,
+                              &error))
+    {
+      g_warning ("Failed to swap buffers: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  gbm_get_next_fb_id (secondary_gpu_state->gpu_kms,
+                      secondary_gpu_state->gbm.surface,
+                      &secondary_gpu_state->gbm.next_bo,
+                      &secondary_gpu_state->gbm.next_fb_id);
+}
+
+static void
+copy_shared_framebuffer_cpu (CoglOnscreen                        *onscreen,
+                             MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state,
+                             MetaRendererNativeGpuData           *renderer_gpu_data)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+  int width, height;
+  uint8_t *target_data;
+  uint32_t target_fb_id;
+  MetaDumbBuffer *next_dumb_fb;
+  MetaDumbBuffer *current_dumb_fb;
+
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+
+  current_dumb_fb = secondary_gpu_state->cpu.dumb_fb;
+  if (current_dumb_fb == &secondary_gpu_state->cpu.dumb_fbs[0])
+    next_dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[1];
+  else
+    next_dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[0];
+  secondary_gpu_state->cpu.dumb_fb = next_dumb_fb;
+
+  target_data = secondary_gpu_state->cpu.dumb_fb->map;
+  target_fb_id = secondary_gpu_state->cpu.dumb_fb->fb_id;
+
+  meta_renderer_native_gles3_read_pixels (egl,
+                                          renderer_native->gles3,
+                                          width, height,
+                                          target_data);
+
+  secondary_gpu_state->gbm.next_fb_id = target_fb_id;
+}
+
+static void
+update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  GHashTableIter iter;
+  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+
+  g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
+  while (g_hash_table_iter_next (&iter,
+                                 NULL,
+                                 (gpointer *) &secondary_gpu_state))
+    {
+      MetaRendererNativeGpuData *renderer_gpu_data;
+
+      renderer_gpu_data = secondary_gpu_state->renderer_gpu_data;
+      switch (renderer_gpu_data->secondary.copy_mode)
+        {
+        case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
+          /* Done after eglSwapBuffers. */
+          break;
+        case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
+          copy_shared_framebuffer_cpu (onscreen,
+                                       secondary_gpu_state,
+                                       renderer_gpu_data);
+          break;
+        }
+    }
+}
+
+static void
+update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen,
+                                              gboolean     *egl_context_changed)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  GHashTableIter iter;
+  MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state;
+
+  g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states);
+  while (g_hash_table_iter_next (&iter,
+                                 NULL,
+                                 (gpointer *) &secondary_gpu_state))
+    {
+      MetaRendererNativeGpuData *renderer_gpu_data;
+
+      renderer_gpu_data =
+        meta_renderer_native_get_gpu_data (renderer_native,
+                                           secondary_gpu_state->gpu_kms);
+      switch (renderer_gpu_data->secondary.copy_mode)
+        {
+        case META_SHARED_FRAMEBUFFER_COPY_MODE_GPU:
+          copy_shared_framebuffer_gpu (onscreen,
+                                       secondary_gpu_state,
+                                       renderer_gpu_data,
+                                       egl_context_changed);
+          break;
+        case META_SHARED_FRAMEBUFFER_COPY_MODE_CPU:
+          /* Done before eglSwapBuffers. */
+          break;
+        }
+    }
+}
+
+static void
 meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
                                                const int    *rectangles,
                                                int           n_rectangles)
 {
   CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
   CoglRenderer *cogl_renderer = cogl_context->display->renderer;
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
-  MetaGpuKms *gpu_kms = onscreen_native->gpu_kms;
-  MetaRendererNativeGpuData *renderer_gpu_data;
+  MetaGpuKms *render_gpu = onscreen_native->render_gpu;
   CoglFrameInfo *frame_info;
+  gboolean egl_context_changed = FALSE;
 
   frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos);
   frame_info->global_frame_counter = renderer_native->frame_counter;
@@ -976,22 +1510,24 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
    * Wait for the flip callback before continuing, as we might have started the
    * animation earlier due to the animation being driven by some other monitor.
    */
-  while (onscreen_native->pending_flips)
-    meta_gpu_kms_wait_for_flip (gpu_kms, NULL);
+  wait_for_pending_flips (onscreen);
+
+  update_secondary_gpu_state_pre_swap_buffers (onscreen);
 
   parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
                                                     rectangles,
                                                     n_rectangles);
 
   renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
-                                                         gpu_kms);
+                                                         render_gpu);
   switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
       g_warn_if_fail (onscreen_native->gbm.next_bo == NULL &&
                       onscreen_native->gbm.next_fb_id == 0);
 
-      if (!gbm_get_next_fb_id (onscreen,
+      if (!gbm_get_next_fb_id (render_gpu,
+                               onscreen_native->gbm.surface,
                                &onscreen_native->gbm.next_bo,
                                &onscreen_native->gbm.next_fb_id))
         return;
@@ -1003,16 +1539,32 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
 #endif
     }
 
+  update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed);
+
   /* If this is the first framebuffer to be presented then we now setup the
    * crtc modes, else we flip from the previous buffer */
   if (onscreen_native->pending_set_crtc)
     {
-      meta_onscreen_native_set_crtc_modes (onscreen_native);
+      meta_onscreen_native_set_crtc_modes (onscreen);
       onscreen_native->pending_set_crtc = FALSE;
     }
 
   onscreen_native->pending_queue_swap_notify_frame_count = renderer_native->frame_counter;
   meta_onscreen_native_flip_crtcs (onscreen);
+
+  /*
+   * If we changed EGL context, cogl will have the wrong idea about what is
+   * current, making it fail to set it when it needs to. Avoid that by making
+   * EGL_NO_CONTEXT current now, making cogl eventually set the correct
+   * context.
+   */
+  if (egl_context_changed)
+    {
+      _cogl_winsys_egl_make_current (cogl_display,
+                                     EGL_NO_SURFACE,
+                                     EGL_NO_SURFACE,
+                                     EGL_NO_CONTEXT);
+    }
 }
 
 static gboolean
@@ -1022,12 +1574,7 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context,
 #ifdef HAVE_EGL_DEVICE
   CoglRenderer *cogl_renderer = cogl_context->display->renderer;
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
-  MetaMonitorManagerKms *monitor_manager_kms =
-    renderer_native->monitor_manager_kms;
-  MetaGpuKms *primary_gpu =
-    meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
-  MetaRendererNativeGpuData *primary_renderer_gpu_data;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
 #endif
 
   COGL_FLAGS_SET (cogl_context->features,
@@ -1044,9 +1591,7 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context,
                   TRUE);
 
 #ifdef HAVE_EGL_DEVICE
-  primary_renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (renderer_native, primary_gpu);
-  if (primary_renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE)
+  if (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE)
     COGL_FLAGS_SET (cogl_context->features,
                     COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, TRUE);
 #endif
@@ -1055,35 +1600,70 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context,
 }
 
 static gboolean
-meta_renderer_native_create_surface_gbm (MetaOnscreenNative  *onscreen_native,
+should_surface_be_sharable (CoglOnscreen *onscreen)
+{
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglRenderer *cogl_renderer = cogl_context->display->renderer;
+  CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+  GList *l;
+
+  if (!onscreen_native->logical_monitor)
+    return FALSE;
+
+  for (l = meta_logical_monitor_get_monitors (onscreen_native->logical_monitor);
+       l;
+       l = l->next)
+    {
+      MetaMonitor *monitor = l->data;
+      MetaGpuKms *gpu_kms = META_GPU_KMS (meta_monitor_get_gpu (monitor));
+
+      if (renderer_gpu_data != meta_renderer_native_get_gpu_data (renderer_native,
+                                                                  gpu_kms))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+meta_renderer_native_create_surface_gbm (CoglOnscreen        *onscreen,
                                          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);
+  CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
+  MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
+  MetaRendererNative *renderer_native = onscreen_native->renderer_native;
+  MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *cogl_context = framebuffer->context;
   CoglDisplay *cogl_display = cogl_context->display;
   CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
-  CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
-  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
-  MetaRendererNativeGpuData *renderer_gpu_data;
+  CoglRenderer *cogl_renderer = cogl_display->renderer;
+  CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
   struct gbm_surface *new_gbm_surface;
   EGLNativeWindowType egl_native_window;
   EGLSurface new_egl_surface;
+  uint32_t flags;
+
+  flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
+  if (should_surface_be_sharable (onscreen))
+    flags |= GBM_BO_USE_LINEAR;
 
   renderer_gpu_data =
     meta_renderer_native_get_gpu_data (renderer_native,
-                                       onscreen_native->gpu_kms);
+                                       onscreen_native->render_gpu);
   new_gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device,
                                         width, height,
                                         GBM_FORMAT_XRGB8888,
-                                        GBM_BO_USE_SCANOUT |
-                                        GBM_BO_USE_RENDERING);
+                                        flags);
 
   if (!new_gbm_surface)
     {
@@ -1115,7 +1695,7 @@ meta_renderer_native_create_surface_gbm (MetaOnscreenNative  *onscreen_native,
 
 #ifdef HAVE_EGL_DEVICE
 static gboolean
-meta_renderer_native_create_surface_egl_device (MetaRendererNative *renderer_native,
+meta_renderer_native_create_surface_egl_device (CoglOnscreen       *onscreen,
                                                 MetaLogicalMonitor *logical_monitor,
                                                 int                 width,
                                                 int                 height,
@@ -1123,15 +1703,16 @@ meta_renderer_native_create_surface_egl_device (MetaRendererNative *renderer_nat
                                                 EGLSurface         *out_egl_surface,
                                                 GError            **error)
 {
-  MetaBackend *backend = meta_get_backend ();
-  MetaEgl *egl = meta_backend_get_egl (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_get_display (cogl_context);
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *cogl_context = framebuffer->context;
+  CoglDisplay *cogl_display = cogl_context->display;
   CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
   CoglRenderer *cogl_renderer = cogl_display->renderer;
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  EGLDisplay egl_display = cogl_renderer_egl->edpy;
+  MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform;
+  MetaEgl *egl =
+    meta_renderer_native_get_egl (renderer_gpu_data->renderer_native);
+  EGLDisplay egl_display = renderer_gpu_data->egl_display;
   MetaMonitor *monitor;
   MetaOutput *output;
   EGLConfig egl_config;
@@ -1386,11 +1967,6 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen,
                                GError      **error)
 {
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-  CoglContext *cogl_context = framebuffer->context;
-  CoglDisplay *cogl_display = cogl_context->display;
-  CoglRenderer *cogl_renderer = cogl_display->renderer;
-  CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native = onscreen_egl->platform;
   MetaRendererNativeGpuData *renderer_gpu_data;
@@ -1417,12 +1993,12 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen,
     return TRUE;
 
   renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (renderer_native,
-                                       onscreen_native->gpu_kms);
+    meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
+                                       onscreen_native->render_gpu);
   switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
-      if (!meta_renderer_native_create_surface_gbm (onscreen_native,
+      if (!meta_renderer_native_create_surface_gbm (onscreen,
                                                     width, height,
                                                     &gbm_surface,
                                                     &egl_surface,
@@ -1435,7 +2011,7 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen,
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       if (!init_dumb_fb (&onscreen_native->egl.dumb_fb,
-                         onscreen_native->gpu_kms,
+                         onscreen_native->render_gpu,
                          width, height,
                          GBM_FORMAT_XRGB8888,
                          error))
@@ -1443,7 +2019,7 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen,
 
       view = onscreen_native->view;
       logical_monitor = meta_renderer_view_get_logical_monitor (view);
-      if (!meta_renderer_native_create_surface_egl_device (renderer_native,
+      if (!meta_renderer_native_create_surface_egl_device (onscreen,
                                                            logical_monitor,
                                                            width, height,
                                                            &egl_stream,
@@ -1467,8 +2043,6 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
   CoglContext *cogl_context = framebuffer->context;
   CoglRenderer *cogl_renderer = cogl_context->display->renderer;
   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
-  MetaRendererNative *renderer_native = cogl_renderer_egl->platform;
-  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
   CoglOnscreenEGL *onscreen_egl = onscreen->winsys;
   MetaOnscreenNative *onscreen_native;
   MetaRendererNativeGpuData *renderer_gpu_data;
@@ -1481,6 +2055,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
 
   if (onscreen_egl->egl_surface != EGL_NO_SURFACE)
     {
+      MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
+
       meta_egl_destroy_surface (egl,
                                 cogl_renderer_egl->edpy,
                                 onscreen_egl->egl_surface,
@@ -1489,8 +2065,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
     }
 
   renderer_gpu_data =
-    meta_renderer_native_get_gpu_data (renderer_native,
-                                       onscreen_native->gpu_kms);
+    meta_renderer_native_get_gpu_data (onscreen_native->renderer_native,
+                                       onscreen_native->render_gpu);
   switch (renderer_gpu_data->mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
@@ -1509,11 +2085,10 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       release_dumb_fb (&onscreen_native->egl.dumb_fb,
-                       onscreen_native->gpu_kms);
+                       onscreen_native->render_gpu);
       if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR)
         {
-          MetaBackend *backend = meta_get_backend ();
-          MetaEgl *egl = meta_backend_get_egl (backend);
+          MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native);
 
           meta_egl_destroy_stream (egl,
                                    cogl_renderer_egl->edpy,
@@ -1525,6 +2100,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen)
 #endif /* HAVE_EGL_DEVICE */
     }
 
+  g_hash_table_destroy (onscreen_native->secondary_gpu_states);
+
   g_slice_free (MetaOnscreenNative, onscreen_native);
   g_slice_free (CoglOnscreenEGL, onscreen->winsys);
   onscreen->winsys = NULL;
@@ -1590,7 +2167,9 @@ meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native)
 }
 
 static CoglOnscreen *
-meta_renderer_native_create_onscreen (MetaGpuKms           *gpu_kms,
+meta_renderer_native_create_onscreen (MetaRendererNative   *renderer_native,
+                                      MetaGpuKms           *render_gpu,
+                                      MetaLogicalMonitor   *logical_monitor,
                                       CoglContext          *context,
                                       MetaMonitorTransform  transform,
                                       gint                  view_width,
@@ -1601,6 +2180,7 @@ meta_renderer_native_create_onscreen (MetaGpuKms           *gpu_kms,
   CoglOnscreenEGL *onscreen_egl;
   MetaOnscreenNative *onscreen_native;
   gint width, height;
+  GList *l;
 
   if (meta_monitor_transform_is_rotated (transform))
     {
@@ -1625,7 +2205,31 @@ meta_renderer_native_create_onscreen (MetaGpuKms           *gpu_kms,
 
   onscreen_egl = onscreen->winsys;
   onscreen_native = onscreen_egl->platform;
-  onscreen_native->gpu_kms = gpu_kms;
+  onscreen_native->renderer_native = renderer_native;
+  onscreen_native->render_gpu = render_gpu;
+  onscreen_native->logical_monitor = logical_monitor;
+  onscreen_native->secondary_gpu_states =
+    g_hash_table_new_full (NULL, NULL,
+                           NULL,
+                           (GDestroyNotify) secondary_gpu_state_free);
+
+  for (l = meta_logical_monitor_get_monitors (logical_monitor); l; l = l->next)
+    {
+      MetaMonitor *monitor = l->data;
+      MetaGpuKms *gpu_kms = META_GPU_KMS (meta_monitor_get_gpu (monitor));
+
+      if (gpu_kms == render_gpu)
+        continue;
+
+      if (get_secondary_gpu_state (onscreen, gpu_kms))
+        continue;
+
+      if (!init_secondary_gpu_state (renderer_native, onscreen, gpu_kms, error))
+        {
+          cogl_object_unref (onscreen);
+          return NULL;
+        }
+    }
 
   return onscreen;
 }
@@ -1696,18 +2300,30 @@ get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer)
 }
 
 static CoglRenderer *
-meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
+create_cogl_renderer_for_gpu (MetaGpuKms *gpu_kms)
 {
   CoglRenderer *cogl_renderer;
 
   cogl_renderer = cogl_renderer_new ();
   cogl_renderer_set_custom_winsys (cogl_renderer,
                                    get_native_cogl_winsys_vtable,
-                                   NULL);
+                                   gpu_kms);
 
   return cogl_renderer;
 }
 
+static CoglRenderer *
+meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
+{
+  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
+  MetaMonitorManagerKms *monitor_manager_kms =
+    renderer_native->monitor_manager_kms;
+  MetaGpuKms *primary_gpu =
+    meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
+
+  return create_cogl_renderer_for_gpu (primary_gpu);
+}
+
 static void
 meta_onscreen_native_set_view (CoglOnscreen     *onscreen,
                                MetaRendererView *view)
@@ -1751,14 +2367,13 @@ meta_renderer_native_create_view (MetaRenderer       *renderer,
     renderer_native->monitor_manager_kms;
   MetaMonitorManager *monitor_manager =
     META_MONITOR_MANAGER (monitor_manager_kms);
-  MetaGpuKms *primary_gpu =
-    meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
   MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
   CoglContext *cogl_context =
     clutter_backend_get_cogl_context (clutter_backend);
   CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
-  CoglDisplayEGL *cogl_display_egl = cogl_display->winsys;
+  MetaGpuKms *primary_gpu;
+  CoglDisplayEGL *cogl_display_egl;
   CoglOnscreenEGL *onscreen_egl;
   MetaMonitorTransform view_transform;
   CoglOnscreen *onscreen = NULL;
@@ -1778,7 +2393,10 @@ meta_renderer_native_create_view (MetaRenderer       *renderer,
   width = roundf (logical_monitor->rect.width * scale);
   height = roundf (logical_monitor->rect.height * scale);
 
-  onscreen = meta_renderer_native_create_onscreen (primary_gpu,
+  primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
+  onscreen = meta_renderer_native_create_onscreen (renderer_native,
+                                                   primary_gpu,
+                                                   logical_monitor,
                                                    cogl_context,
                                                    view_transform,
                                                    width,
@@ -1824,6 +2442,7 @@ meta_renderer_native_create_view (MetaRenderer       *renderer,
 
   /* Ensure we don't point to stale surfaces when creating the offscreen */
   onscreen_egl = onscreen->winsys;
+  cogl_display_egl = cogl_display->winsys;
   _cogl_winsys_egl_make_current (cogl_display,
                                  onscreen_egl->egl_surface,
                                  onscreen_egl->egl_surface,
@@ -1908,21 +2527,146 @@ meta_renderer_native_set_property (GObject      *object,
     }
 }
 
+static gboolean
+create_secondary_egl_config (MetaEgl   *egl,
+                             EGLDisplay egl_display,
+                             EGLConfig *egl_config,
+                             GError   **error)
+{
+  EGLint attributes[] = {
+    EGL_RED_SIZE, 1,
+    EGL_GREEN_SIZE, 1,
+    EGL_BLUE_SIZE, 1,
+    EGL_ALPHA_SIZE, EGL_DONT_CARE,
+    EGL_BUFFER_SIZE, EGL_DONT_CARE,
+    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
+    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+    EGL_NONE
+  };
+
+  return meta_egl_choose_config (egl,
+                                 egl_display,
+                                 attributes,
+                                 egl_config,
+                                 error);
+}
+
+static EGLContext
+create_secondary_egl_context (MetaEgl   *egl,
+                              EGLDisplay egl_display,
+                              EGLConfig  egl_config,
+                              GError   **error)
+{
+  EGLint attributes[] = {
+    EGL_CONTEXT_CLIENT_VERSION, 3,
+    EGL_NONE
+  };
+
+  return meta_egl_create_context (egl,
+                                  egl_display,
+                                  egl_config,
+                                  EGL_NO_CONTEXT,
+                                  attributes,
+                                  error);
+}
+
+static void
+meta_renderer_native_ensure_gles3 (MetaRendererNative *renderer_native)
+{
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+
+  if (renderer_native->gles3)
+    return;
+
+  renderer_native->gles3 = meta_gles3_new (egl);
+}
+
+static gboolean
+init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data,
+                             GError                   **error)
+{
+  MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native;
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+  EGLDisplay egl_display = renderer_gpu_data->egl_display;
+  EGLConfig egl_config;
+  EGLContext egl_context;
+  char **missing_gl_extensions;
+
+  if (!create_secondary_egl_config (egl,egl_display, &egl_config, error))
+    return FALSE;
+
+  egl_context = create_secondary_egl_context (egl, egl_display, egl_config, error);
+  if (egl_context == EGL_NO_CONTEXT)
+    return FALSE;
+
+  meta_renderer_native_ensure_gles3 (renderer_native);
+
+  if (!meta_egl_make_current (egl,
+                              egl_display,
+                              EGL_NO_SURFACE,
+                              EGL_NO_SURFACE,
+                              egl_context,
+                              error))
+    {
+      meta_egl_destroy_context (egl, egl_display, egl_context, NULL);
+      return FALSE;
+    }
+
+  if (!meta_gles3_has_extensions (renderer_native->gles3,
+                                  &missing_gl_extensions,
+                                  "GL_OES_EGL_image_external",
+                                  NULL))
+    {
+      char *missing_gl_extensions_str;
+
+      missing_gl_extensions_str = g_strjoinv (", ", missing_gl_extensions);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Missing OpenGL ES extensions: %s",
+                   missing_gl_extensions_str);
+      g_free (missing_gl_extensions_str);
+      g_free (missing_gl_extensions);
+    }
+
+  renderer_gpu_data->secondary.egl_context = egl_context;
+  renderer_gpu_data->secondary.egl_config = egl_config;
+  renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_GPU;
+
+  return TRUE;
+}
+
+static void
+init_secondary_gpu_data_cpu (MetaRendererNativeGpuData *renderer_gpu_data)
+{
+  renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_CPU;
+}
+
+static void
+init_secondary_gpu_data (MetaRendererNativeGpuData *renderer_gpu_data)
+{
+  GError *error = NULL;
+
+  if (init_secondary_gpu_data_gpu (renderer_gpu_data, &error))
+    return;
+
+  g_warning ("Failed to initialize accelerated iGPU/dGPU framebuffer sharing: %s",
+             error->message);
+  g_error_free (error);
+
+  init_secondary_gpu_data_cpu (renderer_gpu_data);
+}
+
 static MetaRendererNativeGpuData *
 create_renderer_gpu_data_gbm (MetaRendererNative  *renderer_native,
                               MetaGpuKms          *gpu_kms,
                               GError             **error)
 {
-  MetaMonitorManagerKms *monitor_manager_kms =
-    renderer_native->monitor_manager_kms;
-  MetaMonitorManager *monitor_manager =
-    META_MONITOR_MANAGER (monitor_manager_kms);
-  MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager);
-  MetaEgl *egl = meta_backend_get_egl (backend);
+  MetaMonitorManagerKms *monitor_manager_kms;
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
   struct gbm_device *gbm_device;
   EGLDisplay egl_display;
   int kms_fd;
   MetaRendererNativeGpuData *renderer_gpu_data;
+  MetaGpuKms *primary_gpu;
 
   if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL,
                                 "EGL_MESA_platform_gbm",
@@ -1954,12 +2698,19 @@ create_renderer_gpu_data_gbm (MetaRendererNative  *renderer_native,
       return NULL;
     }
 
+  if (!meta_egl_initialize (egl, egl_display, error))
+    return NULL;
+
   renderer_gpu_data = meta_create_renderer_native_gpu_data (gpu_kms);
   renderer_gpu_data->renderer_native = renderer_native;
   renderer_gpu_data->gbm.device = gbm_device;
   renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_GBM;
+  renderer_gpu_data->egl_display = egl_display;
 
-  renderer_native->egl_display = egl_display;
+  monitor_manager_kms = renderer_native->monitor_manager_kms;
+  primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
+  if (gpu_kms != primary_gpu)
+    init_secondary_gpu_data (renderer_gpu_data);
 
   return renderer_gpu_data;
 }
@@ -2085,7 +2836,10 @@ create_renderer_gpu_data_egl_device (MetaRendererNative  *renderer_native,
                                      MetaGpuKms          *gpu_kms,
                                      GError             **error)
 {
+  MetaMonitorManagerKms *monitor_manager_kms =
+    renderer_native->monitor_manager_kms;
   MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+  MetaGpuKms *primary_gpu;
   char **missing_extensions;
   EGLDeviceEXT egl_device;
   EGLDisplay egl_display;
@@ -2099,6 +2853,15 @@ create_renderer_gpu_data_egl_device (MetaRendererNative  *renderer_native,
       return NULL;
     }
 
+  primary_gpu = meta_monitor_manager_kms_get_primary_gpu (monitor_manager_kms);
+  if (gpu_kms != primary_gpu)
+    {
+      g_set_error (error, G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "EGLDevice currently only works with single GPU systems");
+      return NULL;
+    }
+
   egl_device = find_egl_device (renderer_native, gpu_kms, error);
   if (egl_device == EGL_NO_DEVICE_EXT)
     return NULL;
@@ -2139,8 +2902,7 @@ create_renderer_gpu_data_egl_device (MetaRendererNative  *renderer_native,
   renderer_gpu_data->renderer_native = renderer_native;
   renderer_gpu_data->egl.device = egl_device;
   renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_EGL_DEVICE;
-
-  renderer_native->egl_display = egl_display;
+  renderer_gpu_data->egl_display = egl_display;
 
   return renderer_gpu_data;
 }
@@ -2213,29 +2975,25 @@ meta_renderer_native_initable_init (GInitable     *initable,
   MetaMonitorManager *monitor_manager =
     META_MONITOR_MANAGER (monitor_manager_kms);
   GList *gpus;
-  MetaGpuKms *gpu_kms;
-  MetaRendererNativeGpuData *renderer_gpu_data;
+  GList *l;
 
   gpus = meta_monitor_manager_get_gpus (monitor_manager);
-  if (g_list_length (gpus) != 1)
+  for (l = gpus; l; l = l->next)
     {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Renderer only supports single GPU configurations");
-      return FALSE;
-    }
-
-  gpu_kms = META_GPU_KMS (gpus->data);
+      MetaGpuKms *gpu_kms = META_GPU_KMS (l->data);
+      MetaRendererNativeGpuData *renderer_gpu_data;
 
-  renderer_gpu_data =
-    meta_renderer_native_create_renderer_gpu_data (renderer_native,
-                                                   gpu_kms,
-                                                   error);
-  if (!renderer_gpu_data)
-    return FALSE;
+      renderer_gpu_data =
+        meta_renderer_native_create_renderer_gpu_data (renderer_native,
+                                                       gpu_kms,
+                                                       error);
+      if (!renderer_gpu_data)
+        return FALSE;
 
-  g_hash_table_insert (renderer_native->gpu_datas,
-                       gpu_kms,
-                       renderer_gpu_data);
+      g_hash_table_insert (renderer_native->gpu_datas,
+                           gpu_kms,
+                           renderer_gpu_data);
+    }
 
   return TRUE;
 }
@@ -2252,6 +3010,7 @@ meta_renderer_native_finalize (GObject *object)
   MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
 
   g_hash_table_destroy (renderer_native->gpu_datas);
+  g_clear_object (&renderer_native->gles3);
 
   G_OBJECT_CLASS (meta_renderer_native_parent_class)->finalize (object);
 }


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